]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
librados: move C++ APIs into libradospp
authorKefu Chai <kchai@redhat.com>
Tue, 9 Jan 2018 09:19:28 +0000 (17:19 +0800)
committerKefu Chai <kchai@redhat.com>
Thu, 1 Nov 2018 16:15:30 +0000 (00:15 +0800)
the goal is to decouple C++ API from C API, and to version them
differently, as they are targeting different consumers.

this allows us to change the C++ API and bumping up its soversion
without requiring consumer to recompile the librados client for
using the new librados. in this way, C++ API can move faster than
C API. for example, if bufferlist interface is changed for better
performance, and this breaks existing API/ABI, we can bump up
the C++ library's soversion, and and the C library's version unchanged
but ship the new librados's C binding. so the librados client linked
against librados's C library will be able to take advantage of
the improvement in C++ library. while the librados client
linked against C++ library won't break at runtime due to unresolved
symbol or changed structure layout.

this is massive change, the genereal idea is to

* split librados.cc into two source files: librados_c.cc and
  librados_cxx.cc, the former for implementing C APIs, the later
  for C++ APIs.
* extract the C++ API in librados into librados-cxx, the library
  name will be libradospp. but we can change it before nautilus
  is released.
* link these librados libraries with static libraries which it
  depends on, so "-Wl,--exclude-libs,ALL" link flags can help
  hide the non-public symbols.
* extract the tests exercising librados' C++ API into a different
  source file named *_cxx.cc. for instance, to move the C++ tests
  in aio.cc into aio_cxx.cc
* extract the shared helper functions which do not use any librados
  or librados-cxx APIs into test_shared{.cc,h}. the "shared" here
  means, *shared* by C++ and C tests.
* extract the test fixtures, i.e., the subclasses of testing::Test,
  for testing C++ APIs into testcase_cxx.cc.
* update qa/workunits/rados/test.sh accordingly to add the splitted
  tests
* update the consumers of librados to link against librados-cxx
  instead, if they are using the C++ API.

Signed-off-by: Kefu Chai <kchai@redhat.com>
99 files changed:
qa/workunits/rados/test.sh
src/cls/lua/cls_lua_client.h
src/librados/AioCompletionImpl.h
src/librados/CMakeLists.txt
src/librados/RadosClient.cc
src/librados/librados.cc [deleted file]
src/librados/librados_c.cc [new file with mode: 0644]
src/librados/librados_cxx.cc [new file with mode: 0644]
src/librados/librados_tp.cc [new file with mode: 0644]
src/librados/librados_util.cc [new file with mode: 0644]
src/librados/librados_util.h [new file with mode: 0644]
src/libradosstriper/CMakeLists.txt
src/librbd/CMakeLists.txt
src/rbd_replay/CMakeLists.txt
src/rgw/CMakeLists.txt
src/test/CMakeLists.txt
src/test/cls_hello/CMakeLists.txt
src/test/cls_hello/test_cls_hello.cc
src/test/cls_journal/CMakeLists.txt
src/test/cls_journal/test_cls_journal.cc
src/test/cls_lock/CMakeLists.txt
src/test/cls_lock/test_cls_lock.cc
src/test/cls_log/CMakeLists.txt
src/test/cls_log/test_cls_log.cc
src/test/cls_lua/CMakeLists.txt
src/test/cls_lua/test_cls_lua.cc
src/test/cls_numops/CMakeLists.txt
src/test/cls_numops/test_cls_numops.cc
src/test/cls_rbd/CMakeLists.txt
src/test/cls_rbd/test_cls_rbd.cc
src/test/cls_refcount/CMakeLists.txt
src/test/cls_refcount/test_cls_refcount.cc
src/test/cls_rgw/CMakeLists.txt
src/test/cls_rgw/test_cls_rgw.cc
src/test/cls_sdk/CMakeLists.txt
src/test/cls_sdk/test_cls_sdk.cc
src/test/cls_version/CMakeLists.txt
src/test/cls_version/test_cls_version.cc
src/test/journal/CMakeLists.txt
src/test/journal/RadosTestFixture.cc
src/test/librados/CMakeLists.txt
src/test/librados/TestCase.cc
src/test/librados/TestCase.h
src/test/librados/aio.cc
src/test/librados/aio_cxx.cc [new file with mode: 0644]
src/test/librados/c_write_operations.cc
src/test/librados/cls.cc
src/test/librados/cmd.cc
src/test/librados/cmd_cxx.cc [new file with mode: 0644]
src/test/librados/io.cc
src/test/librados/io_cxx.cc [new file with mode: 0644]
src/test/librados/list.cc
src/test/librados/list_cxx.cc [new file with mode: 0644]
src/test/librados/lock.cc
src/test/librados/lock_cxx.cc [new file with mode: 0644]
src/test/librados/misc.cc
src/test/librados/misc_cxx.cc [new file with mode: 0644]
src/test/librados/pool.cc
src/test/librados/service.cc
src/test/librados/service_cxx.cc [new file with mode: 0644]
src/test/librados/snapshots.cc
src/test/librados/snapshots_cxx.cc [new file with mode: 0644]
src/test/librados/stat.cc
src/test/librados/stat_cxx.cc [new file with mode: 0644]
src/test/librados/test.cc
src/test/librados/test.h
src/test/librados/test_cxx.cc [new file with mode: 0644]
src/test/librados/test_cxx.h [new file with mode: 0644]
src/test/librados/test_shared.cc [new file with mode: 0644]
src/test/librados/test_shared.h [new file with mode: 0644]
src/test/librados/testcase_cxx.cc [new file with mode: 0644]
src/test/librados/testcase_cxx.h [new file with mode: 0644]
src/test/librados/tier.cc [deleted file]
src/test/librados/tier_cxx.cc [new file with mode: 0644]
src/test/librados/watch_notify.cc
src/test/librados/watch_notify_cxx.cc [new file with mode: 0644]
src/test/libradosstriper/CMakeLists.txt
src/test/libradosstriper/TestCase.cc
src/test/librbd/CMakeLists.txt
src/test/librbd/test_fixture.cc
src/test/librbd/test_librbd.cc
src/test/librbd/test_main.cc
src/test/multi_stress_watch.cc
src/test/osd/CMakeLists.txt
src/test/rbd_mirror/CMakeLists.txt
src/test/rbd_mirror/test_ClusterWatcher.cc
src/test/rbd_mirror/test_ImageReplayer.cc
src/test/rbd_mirror/test_InstanceWatcher.cc
src/test/rbd_mirror/test_LeaderWatcher.cc
src/test/rbd_mirror/test_PoolWatcher.cc
src/test/rbd_mirror/test_fixture.cc
src/test/rbd_mirror/test_main.cc
src/test/rbd_mirror/test_mock_InstanceWatcher.cc
src/test/test_stress_watch.cc
src/tools/CMakeLists.txt
src/tools/cephfs/CMakeLists.txt
src/tools/rbd/CMakeLists.txt
src/tools/rbd_mirror/CMakeLists.txt
src/tools/rbd_nbd/CMakeLists.txt

index 082c638b0ec3a8d00e93d6c4a4fc507d31dafdbc..a2f5e0499db05cb80de275f414742ef91563719c 100755 (executable)
@@ -15,9 +15,18 @@ trap cleanup EXIT ERR HUP INT QUIT
 declare -A pids
 
 for f in \
-    api_aio api_io api_asio api_list api_lock api_misc \
-    api_tier api_pool api_snapshots api_stat api_watch_notify api_cmd \
-    api_service \
+    api_aio api_aio_pp \
+    api_io api_io_pp \
+    api_asio api_list \
+    api_lock api_lock_pp \
+    api_misc api_misc_pp \
+    api_tier_pp \
+    api_pool \
+    api_snapshots api_snapshots_pp \
+    api_stat api_stat_pp \
+    api_watch_notify api_watch_notify_pp \
+    api_cmd api_cmd_pp \
+    api_service api_service_pp \
     api_c_write_operations \
     api_c_read_operations \
     list_parallel \
index e37906993c9f98cb3d5c2c9818f5506093069fba..c1ed4369f63309eebccd91dbbd166afb2b53bd59 100644 (file)
@@ -2,7 +2,7 @@
 #define CLS_LUA_CLIENT_HPP
 #include <string>
 
-#include "include/buffer_fwd.h"  // for bufferlist
+#include "include/rados/librados.hpp"
 
 namespace librados {
   class IoCtx;
index e7b895358f6afde284091333a6660675e0e3c568..98fa4e0c883dbcdeb30abc204a4e5377bf8cebbf 100644 (file)
@@ -19,8 +19,6 @@
 #include "common/Mutex.h"
 
 #include "include/buffer.h"
-#include "include/rados/librados.h"
-#include "include/rados/librados.hpp"
 #include "include/xlist.h"
 #include "osd/osd_types.h"
 
index 34d6af6a9193a30a22095df46e9eb1de2cb86796..8943e44be23ecb660b91ad5c644b9bd93dd2b78b 100644 (file)
@@ -1,17 +1,27 @@
-add_library(librados_objs OBJECT
+add_library(librados_impl STATIC
   IoCtxImpl.cc
   RadosXattrIter.cc
-  RadosClient.cc)
-add_library(librados_api_obj OBJECT
-  librados.cc)
+  RadosClient.cc
+  librados_util.cc
+  librados_tp.cc
+  $<TARGET_OBJECTS:common_buffer_obj>)
+
+add_library(rados_c_api OBJECT
+  librados_c.cc)
+add_library(rados_cxx_api OBJECT
+  librados_cxx.cc)
+add_library(rados_cxx STATIC
+  $<TARGET_OBJECTS:rados_cxx_api>)
 if(WITH_LTTNG)
-  add_dependencies(librados_api_obj librados-tp)
+  add_dependencies(librados_impl librados-tp)
+  add_dependencies(rados_c_api librados-tp)
+  add_dependencies(rados_cxx_api librados-tp)
 endif()
+
+# C API
+add_library(librados ${CEPH_SHARED}
+  $<TARGET_OBJECTS:rados_c_api>)
 if(ENABLE_SHARED)
-  add_library(librados ${CEPH_SHARED}
-    $<TARGET_OBJECTS:librados_api_obj>
-    $<TARGET_OBJECTS:librados_objs>
-    $<TARGET_OBJECTS:common_buffer_obj>)
   set_target_properties(librados PROPERTIES
     OUTPUT_NAME rados
     VERSION 2.0.0
@@ -22,17 +32,40 @@ if(ENABLE_SHARED)
     set_property(TARGET librados APPEND_STRING PROPERTY
       LINK_FLAGS " -Wl,--exclude-libs,ALL")
   endif()
-else(ENABLE_SHARED)
-  add_library(librados STATIC
-    $<TARGET_OBJECTS:librados_api_obj>
-    $<TARGET_OBJECTS:librados_objs>)
-endif(ENABLE_SHARED)
+  if(WITH_STATIC_LIBSTDCXX)
+    set_property(TARGET librados APPEND_STRING PROPERTY
+      LINK_FLAGS " -static-libstdc++ -static-libgcc")
+  endif()
+endif()
+
 target_link_libraries(librados PRIVATE
+  rados_cxx librados_impl
   osdc ceph-common cls_lock_client
   ${BLKID_LIBRARIES} ${CRYPTO_LIBS} ${EXTRALIBS})
-target_link_libraries(librados ${rados_libs})
 install(TARGETS librados DESTINATION ${CMAKE_INSTALL_LIBDIR})
 
+# C++ API
+add_library(librados-cxx ${CEPH_SHARED}
+  $<TARGET_OBJECTS:rados_cxx_api>)
+if(ENABLE_SHARED)
+  set_target_properties(librados-cxx PROPERTIES
+    OUTPUT_NAME radospp
+    VERSION 1.0.0
+    SOVERSION 1
+    CXX_VISIBILITY_PRESET hidden
+    VISIBILITY_INLINES_HIDDEN ON)
+  if(NOT APPLE)
+    set_property(TARGET librados-cxx APPEND_STRING PROPERTY
+      LINK_FLAGS " -Wl,--exclude-libs,ALL")
+  endif()
+endif(ENABLE_SHARED)
+target_link_libraries(librados-cxx
+  PUBLIC
+    librados
+  PRIVATE
+    librados_impl cls_lock_client ceph-common)
+install(TARGETS librados-cxx DESTINATION ${CMAKE_INSTALL_LIBDIR})
+
 if(WITH_LTTNG AND WITH_EVENTTRACE)
   add_dependencies(librados_api_obj eventtrace_tp)
 endif()
index ff057257b036d4fd0f295967d312f71d7c57df71..4adc3755cf03389bdf69180deaab7b767a54d12b 100644 (file)
@@ -27,6 +27,7 @@
 #include "common/common_init.h"
 #include "common/ceph_json.h"
 #include "common/errno.h"
+#include "common/ceph_json.h"
 #include "include/buffer.h"
 #include "include/stringify.h"
 #include "include/util.h"
@@ -1121,7 +1122,7 @@ mon_feature_t librados::RadosClient::get_required_monitor_features() const
 }
 
 int librados::RadosClient::get_inconsistent_pgs(int64_t pool_id,
-                                                std::vector<std::string>* pgs)
+                                               std::vector<std::string>* pgs)
 {
   vector<string> cmd = {
     "{\"prefix\": \"pg ls\","
diff --git a/src/librados/librados.cc b/src/librados/librados.cc
deleted file mode 100644 (file)
index 67eebe7..0000000
+++ /dev/null
@@ -1,6740 +0,0 @@
-// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
-// vim: ts=8 sw=2 smarttab
-/*
- * Ceph - scalable distributed file system
- *
- * Copyright (C) 2004-2012 Sage Weil <sage@newdream.net>
- *
- * 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.
- *
- */
-
-#include <limits.h>
-
-#include "common/config.h"
-#include "common/errno.h"
-#include "common/ceph_argparse.h"
-#include "common/ceph_json.h"
-#include "common/common_init.h"
-#include "common/TracepointProvider.h"
-#include "common/hobject.h"
-#include "include/rados/librados.h"
-#include "include/rados/librados.hpp"
-#include "include/types.h"
-#include <include/stringify.h>
-
-#include "librados/AioCompletionImpl.h"
-#include "librados/IoCtxImpl.h"
-#include "librados/PoolAsyncCompletionImpl.h"
-#include "librados/RadosClient.h"
-#include "librados/RadosXattrIter.h"
-#include "librados/ListObjectImpl.h"
-#include <cls/lock/cls_lock_client.h>
-
-#include <string>
-#include <map>
-#include <set>
-#include <vector>
-#include <list>
-#include <stdexcept>
-
-#ifdef WITH_LTTNG
-#define TRACEPOINT_DEFINE
-#define TRACEPOINT_PROBE_DYNAMIC_LINKAGE
-#include "tracing/librados.h"
-#undef TRACEPOINT_PROBE_DYNAMIC_LINKAGE
-#undef TRACEPOINT_DEFINE
-#else
-#define tracepoint(...)
-#endif
-
-using std::string;
-using std::map;
-using std::set;
-using std::vector;
-using std::list;
-using std::runtime_error;
-
-#define dout_subsys ceph_subsys_rados
-#undef dout_prefix
-#define dout_prefix *_dout << "librados: "
-
-#define RADOS_LIST_MAX_ENTRIES 1024
-
-namespace {
-
-TracepointProvider::Traits tracepoint_traits("librados_tp.so", "rados_tracing");
-
-uint8_t get_checksum_op_type(rados_checksum_type_t type) {
-  switch (type) {
-  case LIBRADOS_CHECKSUM_TYPE_XXHASH32:
-    return CEPH_OSD_CHECKSUM_OP_TYPE_XXHASH32;
-  case LIBRADOS_CHECKSUM_TYPE_XXHASH64:
-    return CEPH_OSD_CHECKSUM_OP_TYPE_XXHASH64;
-  case LIBRADOS_CHECKSUM_TYPE_CRC32C:
-    return CEPH_OSD_CHECKSUM_OP_TYPE_CRC32C;
-  default:
-    return -1;
-  }
-}
-
-} // anonymous namespace
-
-/*
- * Structure of this file
- *
- * RadosClient and the related classes are the internal implementation of librados.
- * Above that layer sits the C API, found in include/rados/librados.h, and
- * the C++ API, found in include/rados/librados.hpp
- *
- * The C++ API sometimes implements things in terms of the C API.
- * Both the C++ and C API rely on RadosClient.
- *
- * Visually:
- * +--------------------------------------+
- * |             C++ API                  |
- * +--------------------+                 |
- * |       C API        |                 |
- * +--------------------+-----------------+
- * |          RadosClient                 |
- * +--------------------------------------+
- */
-
-namespace librados {
-
-struct ObjectOperationImpl {
-  ::ObjectOperation o;
-  real_time rt;
-  real_time *prt;
-
-  ObjectOperationImpl() : prt(NULL) {}
-};
-
-}
-
-size_t librados::ObjectOperation::size()
-{
-  ::ObjectOperation *o = &impl->o;
-  return o->size();
-}
-
-static void set_op_flags(::ObjectOperation *o, int flags)
-{
-  int rados_flags = 0;
-  if (flags & LIBRADOS_OP_FLAG_EXCL)
-    rados_flags |= CEPH_OSD_OP_FLAG_EXCL;
-  if (flags & LIBRADOS_OP_FLAG_FAILOK)
-    rados_flags |= CEPH_OSD_OP_FLAG_FAILOK;
-  if (flags & LIBRADOS_OP_FLAG_FADVISE_RANDOM)
-    rados_flags |= CEPH_OSD_OP_FLAG_FADVISE_RANDOM;
-  if (flags & LIBRADOS_OP_FLAG_FADVISE_SEQUENTIAL)
-    rados_flags |= CEPH_OSD_OP_FLAG_FADVISE_SEQUENTIAL;
-  if (flags & LIBRADOS_OP_FLAG_FADVISE_WILLNEED)
-    rados_flags |= CEPH_OSD_OP_FLAG_FADVISE_WILLNEED;
-  if (flags & LIBRADOS_OP_FLAG_FADVISE_DONTNEED)
-    rados_flags |= CEPH_OSD_OP_FLAG_FADVISE_DONTNEED;
-  if (flags & LIBRADOS_OP_FLAG_FADVISE_NOCACHE)
-    rados_flags |= CEPH_OSD_OP_FLAG_FADVISE_NOCACHE;
-  o->set_last_op_flags(rados_flags);
-}
-
-//deprcated
-void librados::ObjectOperation::set_op_flags(ObjectOperationFlags flags)
-{
-  ::set_op_flags(&impl->o, (int)flags);
-}
-
-void librados::ObjectOperation::set_op_flags2(int flags)
-{
-  ::ObjectOperation *o = &impl->o;
-  ::set_op_flags(o, flags);
-}
-
-void librados::ObjectOperation::cmpext(uint64_t off,
-                                       bufferlist &cmp_bl,
-                                       int *prval)
-{
-  ::ObjectOperation *o = &impl->o;
-  o->cmpext(off, cmp_bl, prval);
-}
-
-void librados::ObjectOperation::cmpxattr(const char *name, uint8_t op, const bufferlist& v)
-{
-  ::ObjectOperation *o = &impl->o;
-  o->cmpxattr(name, op, CEPH_OSD_CMPXATTR_MODE_STRING, v);
-}
-
-void librados::ObjectOperation::cmpxattr(const char *name, uint8_t op, uint64_t v)
-{
-  ::ObjectOperation *o = &impl->o;
-  bufferlist bl;
-  encode(v, bl);
-  o->cmpxattr(name, op, CEPH_OSD_CMPXATTR_MODE_U64, bl);
-}
-
-void librados::ObjectOperation::assert_version(uint64_t ver)
-{
-  ::ObjectOperation *o = &impl->o;
-  o->assert_version(ver);
-}
-
-void librados::ObjectOperation::assert_exists()
-{
-  ::ObjectOperation *o = &impl->o;
-  o->stat(NULL, (ceph::real_time*) NULL, NULL);
-}
-
-void librados::ObjectOperation::exec(const char *cls, const char *method, bufferlist& inbl)
-{
-  ::ObjectOperation *o = &impl->o;
-  o->call(cls, method, inbl);
-}
-
-void librados::ObjectOperation::exec(const char *cls, const char *method, bufferlist& inbl, bufferlist *outbl, int *prval)
-{
-  ::ObjectOperation *o = &impl->o;
-  o->call(cls, method, inbl, outbl, NULL, prval);
-}
-
-class ObjectOpCompletionCtx : public Context {
-  librados::ObjectOperationCompletion *completion;
-  bufferlist bl;
-public:
-  explicit ObjectOpCompletionCtx(librados::ObjectOperationCompletion *c) : completion(c) {}
-  void finish(int r) override {
-    completion->handle_completion(r, bl);
-    delete completion;
-  }
-
-  bufferlist *outbl() {
-    return &bl;
-  }
-};
-
-void librados::ObjectOperation::exec(const char *cls, const char *method, bufferlist& inbl, librados::ObjectOperationCompletion *completion)
-{
-  ::ObjectOperation *o = &impl->o;
-
-  ObjectOpCompletionCtx *ctx = new ObjectOpCompletionCtx(completion);
-
-  o->call(cls, method, inbl, ctx->outbl(), ctx, NULL);
-}
-
-void librados::ObjectReadOperation::stat(uint64_t *psize, time_t *pmtime, int *prval)
-{
-  ::ObjectOperation *o = &impl->o;
-  o->stat(psize, pmtime, prval);
-}
-
-void librados::ObjectReadOperation::stat2(uint64_t *psize, struct timespec *pts, int *prval)
-{
-  ::ObjectOperation *o = &impl->o;
-  o->stat(psize, pts, prval);
-}
-
-void librados::ObjectReadOperation::read(size_t off, uint64_t len, bufferlist *pbl, int *prval)
-{
-  ::ObjectOperation *o = &impl->o;
-  o->read(off, len, pbl, prval, NULL);
-}
-
-void librados::ObjectReadOperation::sparse_read(uint64_t off, uint64_t len,
-                                               std::map<uint64_t,uint64_t> *m,
-                                               bufferlist *data_bl, int *prval)
-{
-  ::ObjectOperation *o = &impl->o;
-  o->sparse_read(off, len, m, data_bl, prval);
-}
-
-void librados::ObjectReadOperation::checksum(rados_checksum_type_t type,
-                                            const bufferlist &init_value_bl,
-                                            uint64_t off, size_t len,
-                                            size_t chunk_size, bufferlist *pbl,
-                                            int *prval)
-{
-  ::ObjectOperation *o = &impl->o;
-  o->checksum(get_checksum_op_type(type), init_value_bl, off, len, chunk_size,
-             pbl, prval, nullptr);
-}
-
-void librados::ObjectReadOperation::tmap_get(bufferlist *pbl, int *prval)
-{
-  ::ObjectOperation *o = &impl->o;
-  o->tmap_get(pbl, prval);
-}
-
-void librados::ObjectReadOperation::getxattr(const char *name, bufferlist *pbl, int *prval)
-{
-  ::ObjectOperation *o = &impl->o;
-  o->getxattr(name, pbl, prval);
-}
-
-void librados::ObjectReadOperation::omap_get_vals(
-  const std::string &start_after,
-  const std::string &filter_prefix,
-  uint64_t max_return,
-  std::map<std::string, bufferlist> *out_vals,
-  int *prval)
-{
-  ::ObjectOperation *o = &impl->o;
-  o->omap_get_vals(start_after, filter_prefix, max_return, out_vals, nullptr,
-                  prval);
-}
-
-void librados::ObjectReadOperation::omap_get_vals2(
-  const std::string &start_after,
-  const std::string &filter_prefix,
-  uint64_t max_return,
-  std::map<std::string, bufferlist> *out_vals,
-  bool *pmore,
-  int *prval)
-{
-  ::ObjectOperation *o = &impl->o;
-  o->omap_get_vals(start_after, filter_prefix, max_return, out_vals, pmore,
-                  prval);
-}
-
-void librados::ObjectReadOperation::omap_get_vals(
-  const std::string &start_after,
-  uint64_t max_return,
-  std::map<std::string, bufferlist> *out_vals,
-  int *prval)
-{
-  ::ObjectOperation *o = &impl->o;
-  o->omap_get_vals(start_after, "", max_return, out_vals, nullptr, prval);
-}
-
-void librados::ObjectReadOperation::omap_get_vals2(
-  const std::string &start_after,
-  uint64_t max_return,
-  std::map<std::string, bufferlist> *out_vals,
-  bool *pmore,
-  int *prval)
-{
-  ::ObjectOperation *o = &impl->o;
-  o->omap_get_vals(start_after, "", max_return, out_vals, pmore, prval);
-}
-
-void librados::ObjectReadOperation::omap_get_keys(
-  const std::string &start_after,
-  uint64_t max_return,
-  std::set<std::string> *out_keys,
-  int *prval)
-{
-  ::ObjectOperation *o = &impl->o;
-  o->omap_get_keys(start_after, max_return, out_keys, nullptr, prval);
-}
-
-void librados::ObjectReadOperation::omap_get_keys2(
-  const std::string &start_after,
-  uint64_t max_return,
-  std::set<std::string> *out_keys,
-  bool *pmore,
-  int *prval)
-{
-  ::ObjectOperation *o = &impl->o;
-  o->omap_get_keys(start_after, max_return, out_keys, pmore, prval);
-}
-
-void librados::ObjectReadOperation::omap_get_header(bufferlist *bl, int *prval)
-{
-  ::ObjectOperation *o = &impl->o;
-  o->omap_get_header(bl, prval);
-}
-
-void librados::ObjectReadOperation::omap_get_vals_by_keys(
-  const std::set<std::string> &keys,
-  std::map<std::string, bufferlist> *map,
-  int *prval)
-{
-  ::ObjectOperation *o = &impl->o;
-  o->omap_get_vals_by_keys(keys, map, prval);
-}
-
-void librados::ObjectOperation::omap_cmp(
-  const std::map<std::string, pair<bufferlist, int> > &assertions,
-  int *prval)
-{
-  ::ObjectOperation *o = &impl->o;
-  o->omap_cmp(assertions, prval);
-}
-
-void librados::ObjectReadOperation::list_watchers(
-  list<obj_watch_t> *out_watchers,
-  int *prval)
-{
-  ::ObjectOperation *o = &impl->o;
-  o->list_watchers(out_watchers, prval);
-}
-
-void librados::ObjectReadOperation::list_snaps(
-  snap_set_t *out_snaps,
-  int *prval)
-{
-  ::ObjectOperation *o = &impl->o;
-  o->list_snaps(out_snaps, prval);
-}
-
-void librados::ObjectReadOperation::is_dirty(bool *is_dirty, int *prval)
-{
-  ::ObjectOperation *o = &impl->o;
-  o->is_dirty(is_dirty, prval);
-}
-
-int librados::IoCtx::omap_get_vals(const std::string& oid,
-                                   const std::string& orig_start_after,
-                                   const std::string& filter_prefix,
-                                   uint64_t max_return,
-                                   std::map<std::string, bufferlist> *out_vals)
-{
-  bool first = true;
-  string start_after = orig_start_after;
-  bool more = true;
-  while (max_return > 0 && more) {
-    std::map<std::string,bufferlist> out;
-    ObjectReadOperation op;
-    op.omap_get_vals2(start_after, filter_prefix, max_return, &out, &more,
-                     nullptr);
-    bufferlist bl;
-    int ret = operate(oid, &op, &bl);
-    if (ret < 0) {
-      return ret;
-    }
-    if (more) {
-      if (out.empty()) {
-       return -EINVAL;  // wth
-      }
-      start_after = out.rbegin()->first;
-    }
-    if (out.size() <= max_return) {
-      max_return -= out.size();
-    } else {
-      max_return = 0;
-    }
-    if (first) {
-      out_vals->swap(out);
-      first = false;
-    } else {
-      out_vals->insert(out.begin(), out.end());
-      out.clear();
-    }
-  }
-  return 0;
-}
-
-int librados::IoCtx::omap_get_vals2(
-  const std::string& oid,
-  const std::string& start_after,
-  const std::string& filter_prefix,
-  uint64_t max_return,
-  std::map<std::string, bufferlist> *out_vals,
-  bool *pmore)
-{
-  ObjectReadOperation op;
-  int r;
-  op.omap_get_vals2(start_after, filter_prefix, max_return, out_vals, pmore, &r);
-  bufferlist bl;
-  int ret = operate(oid, &op, &bl);
-  if (ret < 0)
-    return ret;
-  return r;
-}
-
-void librados::ObjectReadOperation::getxattrs(map<string, bufferlist> *pattrs, int *prval)
-{
-  ::ObjectOperation *o = &impl->o;
-  o->getxattrs(pattrs, prval);
-}
-
-void librados::ObjectWriteOperation::mtime(time_t *pt)
-{
-  if (pt) {
-    impl->rt = ceph::real_clock::from_time_t(*pt);
-    impl->prt = &impl->rt;
-  }
-}
-
-void librados::ObjectWriteOperation::mtime2(struct timespec *pts)
-{
-  if (pts) {
-    impl->rt = ceph::real_clock::from_timespec(*pts);
-    impl->prt = &impl->rt;
-  }
-}
-
-void librados::ObjectWriteOperation::create(bool exclusive)
-{
-  ::ObjectOperation *o = &impl->o;
-  o->create(exclusive);
-}
-
-void librados::ObjectWriteOperation::create(bool exclusive,
-                                           const std::string& category) // unused
-{
-  ::ObjectOperation *o = &impl->o;
-  o->create(exclusive);
-}
-
-void librados::ObjectWriteOperation::write(uint64_t off, const bufferlist& bl)
-{
-  ::ObjectOperation *o = &impl->o;
-  bufferlist c = bl;
-  o->write(off, c);
-}
-
-void librados::ObjectWriteOperation::write_full(const bufferlist& bl)
-{
-  ::ObjectOperation *o = &impl->o;
-  bufferlist c = bl;
-  o->write_full(c);
-}
-
-void librados::ObjectWriteOperation::writesame(uint64_t off, uint64_t write_len,
-                                              const bufferlist& bl)
-{
-  ::ObjectOperation *o = &impl->o;
-  bufferlist c = bl;
-  o->writesame(off, write_len, c);
-}
-
-void librados::ObjectWriteOperation::append(const bufferlist& bl)
-{
-  ::ObjectOperation *o = &impl->o;
-  bufferlist c = bl;
-  o->append(c);
-}
-
-void librados::ObjectWriteOperation::remove()
-{
-  ::ObjectOperation *o = &impl->o;
-  o->remove();
-}
-
-void librados::ObjectWriteOperation::truncate(uint64_t off)
-{
-  ::ObjectOperation *o = &impl->o;
-  o->truncate(off);
-}
-
-void librados::ObjectWriteOperation::zero(uint64_t off, uint64_t len)
-{
-  ::ObjectOperation *o = &impl->o;
-  o->zero(off, len);
-}
-
-void librados::ObjectWriteOperation::rmxattr(const char *name)
-{
-  ::ObjectOperation *o = &impl->o;
-  o->rmxattr(name);
-}
-
-void librados::ObjectWriteOperation::setxattr(const char *name, const bufferlist& v)
-{
-  ::ObjectOperation *o = &impl->o;
-  o->setxattr(name, v);
-}
-
-void librados::ObjectWriteOperation::omap_set(
-  const map<string, bufferlist> &map)
-{
-  ::ObjectOperation *o = &impl->o;
-  o->omap_set(map);
-}
-
-void librados::ObjectWriteOperation::omap_set_header(const bufferlist &bl)
-{
-  bufferlist c = bl;
-  ::ObjectOperation *o = &impl->o;
-  o->omap_set_header(c);
-}
-
-void librados::ObjectWriteOperation::omap_clear()
-{
-  ::ObjectOperation *o = &impl->o;
-  o->omap_clear();
-}
-
-void librados::ObjectWriteOperation::omap_rm_keys(
-  const std::set<std::string> &to_rm)
-{
-  ::ObjectOperation *o = &impl->o;
-  o->omap_rm_keys(to_rm);
-}
-
-void librados::ObjectWriteOperation::copy_from(const std::string& src,
-                                               const IoCtx& src_ioctx,
-                                               uint64_t src_version)
-{
-  copy_from2(src, src_ioctx, src_version, 0);
-}
-
-void librados::ObjectWriteOperation::copy_from2(const std::string& src,
-                                               const IoCtx& src_ioctx,
-                                               uint64_t src_version,
-                                               uint32_t src_fadvise_flags)
-{
-  ::ObjectOperation *o = &impl->o;
-  o->copy_from(object_t(src), src_ioctx.io_ctx_impl->snap_seq,
-              src_ioctx.io_ctx_impl->oloc, src_version, 0, src_fadvise_flags);
-}
-
-void librados::ObjectWriteOperation::undirty()
-{
-  ::ObjectOperation *o = &impl->o;
-  o->undirty();
-}
-
-void librados::ObjectReadOperation::cache_flush()
-{
-  ::ObjectOperation *o = &impl->o;
-  o->cache_flush();
-}
-
-void librados::ObjectReadOperation::cache_try_flush()
-{
-  ::ObjectOperation *o = &impl->o;
-  o->cache_try_flush();
-}
-
-void librados::ObjectReadOperation::cache_evict()
-{
-  ::ObjectOperation *o = &impl->o;
-  o->cache_evict();
-}
-
-void librados::ObjectWriteOperation::set_redirect(const std::string& tgt_obj, 
-                                                 const IoCtx& tgt_ioctx,
-                                                 uint64_t tgt_version,
-                                                 int flag)
-{
-  ::ObjectOperation *o = &impl->o;
-  o->set_redirect(object_t(tgt_obj), tgt_ioctx.io_ctx_impl->snap_seq,
-                         tgt_ioctx.io_ctx_impl->oloc, tgt_version, flag);
-}
-
-void librados::ObjectWriteOperation::set_chunk(uint64_t src_offset,
-                                              uint64_t src_length,
-                                              const IoCtx& tgt_ioctx,
-                                              string tgt_oid,
-                                              uint64_t tgt_offset,
-                                              int flag)
-{
-  ::ObjectOperation *o = &impl->o;
-  o->set_chunk(src_offset, src_length, 
-              tgt_ioctx.io_ctx_impl->oloc, object_t(tgt_oid), tgt_offset, flag);
-}
-
-void librados::ObjectWriteOperation::tier_promote()
-{
-  ::ObjectOperation *o = &impl->o;
-  o->tier_promote();
-}
-
-void librados::ObjectWriteOperation::unset_manifest()
-{
-  ::ObjectOperation *o = &impl->o;
-  o->unset_manifest();
-}
-
-void librados::ObjectWriteOperation::tmap_put(const bufferlist &bl)
-{
-  ::ObjectOperation *o = &impl->o;
-  bufferlist c = bl;
-  o->tmap_put(c);
-}
-
-void librados::ObjectWriteOperation::tmap_update(const bufferlist& cmdbl)
-{
-  ::ObjectOperation *o = &impl->o;
-  bufferlist c = cmdbl;
-  o->tmap_update(c);
-}
-
-void librados::ObjectWriteOperation::selfmanaged_snap_rollback(snap_t snapid)
-{
-  ::ObjectOperation *o = &impl->o;
-  o->rollback(snapid);
-}
-
-// You must specify the snapid not the name normally used with pool snapshots
-void librados::ObjectWriteOperation::snap_rollback(snap_t snapid)
-{
-  ::ObjectOperation *o = &impl->o;
-  o->rollback(snapid);
-}
-
-void librados::ObjectWriteOperation::set_alloc_hint(
-                                            uint64_t expected_object_size,
-                                            uint64_t expected_write_size)
-{
-  ::ObjectOperation *o = &impl->o;
-  o->set_alloc_hint(expected_object_size, expected_write_size, 0);
-}
-void librados::ObjectWriteOperation::set_alloc_hint2(
-                                            uint64_t expected_object_size,
-                                            uint64_t expected_write_size,
-                                           uint32_t flags)
-{
-  ::ObjectOperation *o = &impl->o;
-  o->set_alloc_hint(expected_object_size, expected_write_size, flags);
-}
-
-void librados::ObjectWriteOperation::cache_pin()
-{
-  ::ObjectOperation *o = &impl->o;
-  o->cache_pin();
-}
-
-void librados::ObjectWriteOperation::cache_unpin()
-{
-  ::ObjectOperation *o = &impl->o;
-  o->cache_unpin();
-}
-
-librados::WatchCtx::
-~WatchCtx()
-{
-}
-
-librados::WatchCtx2::
-~WatchCtx2()
-{
-}
-
-
-struct librados::ObjListCtx {
-  librados::IoCtxImpl dupctx;
-  librados::IoCtxImpl *ctx;
-  Objecter::NListContext *nlc;
-  bool legacy_list_api;
-
-  ObjListCtx(IoCtxImpl *c, Objecter::NListContext *nl, bool legacy=false)
-    : nlc(nl),
-      legacy_list_api(legacy) {
-    // Get our own private IoCtxImpl so that namespace setting isn't
-    // changed by caller between uses.
-    ctx = &dupctx;
-    dupctx.dup(*c);
-  }
-  ~ObjListCtx() {
-    ctx = NULL;
-    delete nlc;
-  }
-};
-
-///////////////////////////// NObjectIteratorImpl /////////////////////////////
-librados::NObjectIteratorImpl::NObjectIteratorImpl(ObjListCtx *ctx_)
-  : ctx(ctx_)
-{
-}
-
-librados::NObjectIteratorImpl::~NObjectIteratorImpl()
-{
-  ctx.reset();
-}
-
-librados::NObjectIteratorImpl::NObjectIteratorImpl(const NObjectIteratorImpl &rhs)
-{
-  *this = rhs;
-}
-
-librados::NObjectIteratorImpl& librados::NObjectIteratorImpl::operator=(const librados::NObjectIteratorImpl &rhs)
-{
-  if (&rhs == this)
-    return *this;
-  if (rhs.ctx.get() == NULL) {
-    ctx.reset();
-    return *this;
-  }
-  Objecter::NListContext *list_ctx = new Objecter::NListContext(*rhs.ctx->nlc);
-  ctx.reset(new ObjListCtx(rhs.ctx->ctx, list_ctx));
-  cur_obj = rhs.cur_obj;
-  return *this;
-}
-
-bool librados::NObjectIteratorImpl::operator==(const librados::NObjectIteratorImpl& rhs) const {
-
-  if (ctx.get() == NULL) {
-    if (rhs.ctx.get() == NULL)
-      return true;
-    return rhs.ctx->nlc->at_end();
-  }
-  if (rhs.ctx.get() == NULL) {
-    // Redundant but same as ObjectIterator version
-    if (ctx.get() == NULL)
-      return true;
-    return ctx->nlc->at_end();
-  }
-  return ctx.get() == rhs.ctx.get();
-}
-
-bool librados::NObjectIteratorImpl::operator!=(const librados::NObjectIteratorImpl& rhs) const {
-  return !(*this == rhs);
-}
-
-const librados::ListObject& librados::NObjectIteratorImpl::operator*() const {
-  return cur_obj;
-}
-
-const librados::ListObject* librados::NObjectIteratorImpl::operator->() const {
-  return &cur_obj;
-}
-
-librados::NObjectIteratorImpl& librados::NObjectIteratorImpl::operator++()
-{
-  get_next();
-  return *this;
-}
-
-librados::NObjectIteratorImpl librados::NObjectIteratorImpl::operator++(int)
-{
-  librados::NObjectIteratorImpl ret(*this);
-  get_next();
-  return ret;
-}
-
-uint32_t librados::NObjectIteratorImpl::seek(uint32_t pos)
-{
-  uint32_t r = rados_nobjects_list_seek(ctx.get(), pos);
-  get_next();
-  return r;
-}
-
-uint32_t librados::NObjectIteratorImpl::seek(const ObjectCursor& cursor)
-{
-  uint32_t r = rados_nobjects_list_seek_cursor(ctx.get(), (rados_object_list_cursor)cursor.c_cursor);
-  get_next();
-  return r;
-}
-
-librados::ObjectCursor librados::NObjectIteratorImpl::get_cursor()
-{
-  librados::ObjListCtx *lh = (librados::ObjListCtx *)ctx.get();
-  librados::ObjectCursor oc;
-  oc.set(lh->ctx->nlist_get_cursor(lh->nlc));
-  return oc;
-}
-
-void librados::NObjectIteratorImpl::set_filter(const bufferlist &bl)
-{
-  ceph_assert(ctx);
-  ctx->nlc->filter = bl;
-}
-
-void librados::NObjectIteratorImpl::get_next()
-{
-  const char *entry, *key, *nspace;
-  if (ctx->nlc->at_end())
-    return;
-  int ret = rados_nobjects_list_next(ctx.get(), &entry, &key, &nspace);
-  if (ret == -ENOENT) {
-    return;
-  }
-  else if (ret) {
-    ostringstream oss;
-    oss << "rados returned " << cpp_strerror(ret);
-    throw std::runtime_error(oss.str());
-  }
-
-  if (cur_obj.impl == NULL)
-    cur_obj.impl = new ListObjectImpl();
-  cur_obj.impl->nspace = nspace;
-  cur_obj.impl->oid = entry;
-  cur_obj.impl->locator = key ? key : string();
-}
-
-uint32_t librados::NObjectIteratorImpl::get_pg_hash_position() const
-{
-  return ctx->nlc->get_pg_hash_position();
-}
-
-///////////////////////////// NObjectIterator /////////////////////////////
-librados::NObjectIterator::NObjectIterator(ObjListCtx *ctx_)
-{
-  impl = new NObjectIteratorImpl(ctx_);
-}
-
-librados::NObjectIterator::~NObjectIterator()
-{
-  delete impl;
-}
-
-librados::NObjectIterator::NObjectIterator(const NObjectIterator &rhs)
-{
-  if (rhs.impl == NULL) {
-    impl = NULL;
-    return;
-  }
-  impl = new NObjectIteratorImpl();
-  *impl = *(rhs.impl);
-}
-
-librados::NObjectIterator& librados::NObjectIterator::operator=(const librados::NObjectIterator &rhs)
-{
-  if (rhs.impl == NULL) {
-    delete impl;
-    impl = NULL;
-    return *this;
-  }
-  if (impl == NULL)
-    impl = new NObjectIteratorImpl();
-  *impl = *(rhs.impl);
-  return *this;
-}
-
-bool librados::NObjectIterator::operator==(const librados::NObjectIterator& rhs) const 
-{
-  if (impl && rhs.impl) {
-    return *impl == *(rhs.impl);
-  } else {
-    return impl == rhs.impl;
-  }
-}
-
-bool librados::NObjectIterator::operator!=(const librados::NObjectIterator& rhs) const
-{
-  return !(*this == rhs);
-}
-
-const librados::ListObject& librados::NObjectIterator::operator*() const {
-  ceph_assert(impl);
-  return *(impl->get_listobjectp());
-}
-
-const librados::ListObject* librados::NObjectIterator::operator->() const {
-  ceph_assert(impl);
-  return impl->get_listobjectp();
-}
-
-librados::NObjectIterator& librados::NObjectIterator::operator++()
-{
-  ceph_assert(impl);
-  impl->get_next();
-  return *this;
-}
-
-librados::NObjectIterator librados::NObjectIterator::operator++(int)
-{
-  librados::NObjectIterator ret(*this);
-  impl->get_next();
-  return ret;
-}
-
-uint32_t librados::NObjectIterator::seek(uint32_t pos)
-{
-  ceph_assert(impl);
-  return impl->seek(pos);
-}
-
-uint32_t librados::NObjectIterator::seek(const ObjectCursor& cursor)
-{
-  ceph_assert(impl);
-  return impl->seek(cursor);
-}
-
-librados::ObjectCursor librados::NObjectIterator::get_cursor()
-{
-  ceph_assert(impl);
-  return impl->get_cursor();
-}
-
-void librados::NObjectIterator::set_filter(const bufferlist &bl)
-{
-  impl->set_filter(bl);
-}
-
-void librados::NObjectIterator::get_next()
-{
-  ceph_assert(impl);
-  impl->get_next();
-}
-
-uint32_t librados::NObjectIterator::get_pg_hash_position() const
-{
-  ceph_assert(impl);
-  return impl->get_pg_hash_position();
-}
-
-const librados::NObjectIterator librados::NObjectIterator::__EndObjectIterator(NULL);
-
-///////////////////////////// PoolAsyncCompletion //////////////////////////////
-int librados::PoolAsyncCompletion::PoolAsyncCompletion::set_callback(void *cb_arg,
-                                                                    rados_callback_t cb)
-{
-  PoolAsyncCompletionImpl *c = (PoolAsyncCompletionImpl *)pc;
-  return c->set_callback(cb_arg, cb);
-}
-
-int librados::PoolAsyncCompletion::PoolAsyncCompletion::wait()
-{
-  PoolAsyncCompletionImpl *c = (PoolAsyncCompletionImpl *)pc;
-  return c->wait();
-}
-
-bool librados::PoolAsyncCompletion::PoolAsyncCompletion::is_complete()
-{
-  PoolAsyncCompletionImpl *c = (PoolAsyncCompletionImpl *)pc;
-  return c->is_complete();
-}
-
-int librados::PoolAsyncCompletion::PoolAsyncCompletion::get_return_value()
-{
-  PoolAsyncCompletionImpl *c = (PoolAsyncCompletionImpl *)pc;
-  return c->get_return_value();
-}
-
-void librados::PoolAsyncCompletion::PoolAsyncCompletion::release()
-{
-  PoolAsyncCompletionImpl *c = (PoolAsyncCompletionImpl *)pc;
-  c->release();
-  delete this;
-}
-
-///////////////////////////// AioCompletion //////////////////////////////
-int librados::AioCompletion::AioCompletion::set_complete_callback(void *cb_arg, rados_callback_t cb)
-{
-  AioCompletionImpl *c = (AioCompletionImpl *)pc;
-  return c->set_complete_callback(cb_arg, cb);
-}
-
-int librados::AioCompletion::AioCompletion::set_safe_callback(void *cb_arg, rados_callback_t cb)
-{
-  AioCompletionImpl *c = (AioCompletionImpl *)pc;
-  return c->set_safe_callback(cb_arg, cb);
-}
-
-int librados::AioCompletion::AioCompletion::wait_for_complete()
-{
-  AioCompletionImpl *c = (AioCompletionImpl *)pc;
-  return c->wait_for_complete();
-}
-
-int librados::AioCompletion::AioCompletion::wait_for_safe()
-{
-  AioCompletionImpl *c = (AioCompletionImpl *)pc;
-  return c->wait_for_safe();
-}
-
-bool librados::AioCompletion::AioCompletion::is_complete()
-{
-  AioCompletionImpl *c = (AioCompletionImpl *)pc;
-  return c->is_complete();
-}
-
-bool librados::AioCompletion::AioCompletion::is_safe()
-{
-  AioCompletionImpl *c = (AioCompletionImpl *)pc;
-  return c->is_safe();
-}
-
-int librados::AioCompletion::AioCompletion::wait_for_complete_and_cb()
-{
-  AioCompletionImpl *c = (AioCompletionImpl *)pc;
-  return c->wait_for_complete_and_cb();
-}
-
-int librados::AioCompletion::AioCompletion::wait_for_safe_and_cb()
-{
-  AioCompletionImpl *c = (AioCompletionImpl *)pc;
-  return c->wait_for_safe_and_cb();
-}
-
-bool librados::AioCompletion::AioCompletion::is_complete_and_cb()
-{
-  AioCompletionImpl *c = (AioCompletionImpl *)pc;
-  return c->is_complete_and_cb();
-}
-
-bool librados::AioCompletion::AioCompletion::is_safe_and_cb()
-{
-  AioCompletionImpl *c = (AioCompletionImpl *)pc;
-  return c->is_safe_and_cb();
-}
-
-int librados::AioCompletion::AioCompletion::get_return_value()
-{
-  AioCompletionImpl *c = (AioCompletionImpl *)pc;
-  return c->get_return_value();
-}
-
-int librados::AioCompletion::AioCompletion::get_version()
-{
-  AioCompletionImpl *c = (AioCompletionImpl *)pc;
-  return c->get_version();
-}
-
-uint64_t librados::AioCompletion::AioCompletion::get_version64()
-{
-  AioCompletionImpl *c = (AioCompletionImpl *)pc;
-  return c->get_version();
-}
-
-void librados::AioCompletion::AioCompletion::release()
-{
-  AioCompletionImpl *c = (AioCompletionImpl *)pc;
-  c->release();
-  delete this;
-}
-
-///////////////////////////// IoCtx //////////////////////////////
-librados::IoCtx::IoCtx() : io_ctx_impl(NULL)
-{
-}
-
-void librados::IoCtx::from_rados_ioctx_t(rados_ioctx_t p, IoCtx &io)
-{
-  IoCtxImpl *io_ctx_impl = (IoCtxImpl*)p;
-
-  io.io_ctx_impl = io_ctx_impl;
-  if (io_ctx_impl) {
-    io_ctx_impl->get();
-  }
-}
-
-librados::IoCtx::IoCtx(const IoCtx& rhs)
-{
-  io_ctx_impl = rhs.io_ctx_impl;
-  if (io_ctx_impl) {
-    io_ctx_impl->get();
-  }
-}
-
-librados::IoCtx& librados::IoCtx::operator=(const IoCtx& rhs)
-{
-  if (io_ctx_impl)
-    io_ctx_impl->put();
-  io_ctx_impl = rhs.io_ctx_impl;
-  io_ctx_impl->get();
-  return *this;
-}
-
-librados::IoCtx::~IoCtx()
-{
-  close();
-}
-
-void librados::IoCtx::close()
-{
-  if (io_ctx_impl)
-    io_ctx_impl->put();
-  io_ctx_impl = 0;
-}
-
-void librados::IoCtx::dup(const IoCtx& rhs)
-{
-  if (io_ctx_impl)
-    io_ctx_impl->put();
-  io_ctx_impl = new IoCtxImpl();
-  io_ctx_impl->get();
-  io_ctx_impl->dup(*rhs.io_ctx_impl);
-}
-
-int librados::IoCtx::set_auid(uint64_t auid_)
-{
-  return -EOPNOTSUPP;
-}
-
-int librados::IoCtx::set_auid_async(uint64_t auid_, PoolAsyncCompletion *c)
-{
-  return -EOPNOTSUPP;
-}
-
-int librados::IoCtx::get_auid(uint64_t *auid_)
-{
-  return -EOPNOTSUPP;
-}
-
-bool librados::IoCtx::pool_requires_alignment()
-{
-  return io_ctx_impl->client->pool_requires_alignment(get_id());
-}
-
-int librados::IoCtx::pool_requires_alignment2(bool *requires)
-{
-  return io_ctx_impl->client->pool_requires_alignment2(get_id(), requires);
-}
-
-uint64_t librados::IoCtx::pool_required_alignment()
-{
-  return io_ctx_impl->client->pool_required_alignment(get_id());
-}
-
-int librados::IoCtx::pool_required_alignment2(uint64_t *alignment)
-{
-  return io_ctx_impl->client->pool_required_alignment2(get_id(), alignment);
-}
-
-std::string librados::IoCtx::get_pool_name()
-{
-  std::string s;
-  io_ctx_impl->client->pool_get_name(get_id(), &s);
-  return s;
-}
-
-std::string librados::IoCtx::get_pool_name() const
-{
-  return io_ctx_impl->get_cached_pool_name();
-}
-
-uint64_t librados::IoCtx::get_instance_id() const
-{
-  return io_ctx_impl->client->get_instance_id();
-}
-
-int librados::IoCtx::create(const std::string& oid, bool exclusive)
-{
-  object_t obj(oid);
-  return io_ctx_impl->create(obj, exclusive);
-}
-
-int librados::IoCtx::create(const std::string& oid, bool exclusive,
-                           const std::string& category) // unused
-{
-  object_t obj(oid);
-  return io_ctx_impl->create(obj, exclusive);
-}
-
-int librados::IoCtx::write(const std::string& oid, bufferlist& bl, size_t len, uint64_t off)
-{
-  object_t obj(oid);
-  return io_ctx_impl->write(obj, bl, len, off);
-}
-
-int librados::IoCtx::append(const std::string& oid, bufferlist& bl, size_t len)
-{
-  object_t obj(oid);
-  return io_ctx_impl->append(obj, bl, len);
-}
-
-int librados::IoCtx::write_full(const std::string& oid, bufferlist& bl)
-{
-  object_t obj(oid);
-  return io_ctx_impl->write_full(obj, bl);
-}
-
-int librados::IoCtx::writesame(const std::string& oid, bufferlist& bl,
-                              size_t write_len, uint64_t off)
-{
-  object_t obj(oid);
-  return io_ctx_impl->writesame(obj, bl, write_len, off);
-}
-
-
-int librados::IoCtx::read(const std::string& oid, bufferlist& bl, size_t len, uint64_t off)
-{
-  object_t obj(oid);
-  return io_ctx_impl->read(obj, bl, len, off);
-}
-
-int librados::IoCtx::checksum(const std::string& oid,
-                             rados_checksum_type_t type,
-                             const bufferlist &init_value_bl, size_t len,
-                             uint64_t off, size_t chunk_size, bufferlist *pbl)
-{
-  object_t obj(oid);
-  return io_ctx_impl->checksum(obj, get_checksum_op_type(type), init_value_bl,
-                              len, off, chunk_size, pbl);
-}
-
-int librados::IoCtx::remove(const std::string& oid)
-{
-  object_t obj(oid);
-  return io_ctx_impl->remove(obj);
-}
-
-int librados::IoCtx::remove(const std::string& oid, int flags)
-{
-  object_t obj(oid);
-  return io_ctx_impl->remove(obj, flags); 
-}
-
-int librados::IoCtx::trunc(const std::string& oid, uint64_t size)
-{
-  object_t obj(oid);
-  return io_ctx_impl->trunc(obj, size);
-}
-
-int librados::IoCtx::mapext(const std::string& oid, uint64_t off, size_t len,
-                           std::map<uint64_t,uint64_t>& m)
-{
-  object_t obj(oid);
-  return io_ctx_impl->mapext(obj, off, len, m);
-}
-
-int librados::IoCtx::cmpext(const std::string& oid, uint64_t off, bufferlist& cmp_bl)
-{
-  object_t obj(oid);
-  return io_ctx_impl->cmpext(obj, off, cmp_bl);
-}
-
-int librados::IoCtx::sparse_read(const std::string& oid, std::map<uint64_t,uint64_t>& m,
-                                bufferlist& bl, size_t len, uint64_t off)
-{
-  object_t obj(oid);
-  return io_ctx_impl->sparse_read(obj, m, bl, len, off);
-}
-
-int librados::IoCtx::getxattr(const std::string& oid, const char *name, bufferlist& bl)
-{
-  object_t obj(oid);
-  return io_ctx_impl->getxattr(obj, name, bl);
-}
-
-int librados::IoCtx::getxattrs(const std::string& oid, map<std::string, bufferlist>& attrset)
-{
-  object_t obj(oid);
-  return io_ctx_impl->getxattrs(obj, attrset);
-}
-
-int librados::IoCtx::setxattr(const std::string& oid, const char *name, bufferlist& bl)
-{
-  object_t obj(oid);
-  return io_ctx_impl->setxattr(obj, name, bl);
-}
-
-int librados::IoCtx::rmxattr(const std::string& oid, const char *name)
-{
-  object_t obj(oid);
-  return io_ctx_impl->rmxattr(obj, name);
-}
-
-int librados::IoCtx::stat(const std::string& oid, uint64_t *psize, time_t *pmtime)
-{
-  object_t obj(oid);
-  return io_ctx_impl->stat(obj, psize, pmtime);
-}
-
-int librados::IoCtx::stat2(const std::string& oid, uint64_t *psize, struct timespec *pts)
-{
-  object_t obj(oid);
-  return io_ctx_impl->stat2(obj, psize, pts);
-}
-
-int librados::IoCtx::exec(const std::string& oid, const char *cls, const char *method,
-                         bufferlist& inbl, bufferlist& outbl)
-{
-  object_t obj(oid);
-  return io_ctx_impl->exec(obj, cls, method, inbl, outbl);
-}
-
-int librados::IoCtx::tmap_update(const std::string& oid, bufferlist& cmdbl)
-{
-  object_t obj(oid);
-  return io_ctx_impl->tmap_update(obj, cmdbl);
-}
-
-int librados::IoCtx::tmap_put(const std::string& oid, bufferlist& bl)
-{
-  object_t obj(oid);
-  return io_ctx_impl->tmap_put(obj, bl);
-}
-
-int librados::IoCtx::tmap_get(const std::string& oid, bufferlist& bl)
-{
-  object_t obj(oid);
-  return io_ctx_impl->tmap_get(obj, bl);
-}
-
-int librados::IoCtx::tmap_to_omap(const std::string& oid, bool nullok)
-{
-  object_t obj(oid);
-  return io_ctx_impl->tmap_to_omap(obj, nullok);
-}
-
-int librados::IoCtx::omap_get_vals(const std::string& oid,
-                                   const std::string& start_after,
-                                   uint64_t max_return,
-                                   std::map<std::string, bufferlist> *out_vals)
-{
-  return omap_get_vals(oid, start_after, string(), max_return, out_vals);
-}
-
-int librados::IoCtx::omap_get_vals2(
-  const std::string& oid,
-  const std::string& start_after,
-  uint64_t max_return,
-  std::map<std::string, bufferlist> *out_vals,
-  bool *pmore)
-{
-  ObjectReadOperation op;
-  int r;
-  op.omap_get_vals2(start_after, max_return, out_vals, pmore, &r);
-  bufferlist bl;
-  int ret = operate(oid, &op, &bl);
-  if (ret < 0)
-    return ret;
-  return r;
-}
-
-int librados::IoCtx::omap_get_keys(const std::string& oid,
-                                   const std::string& orig_start_after,
-                                   uint64_t max_return,
-                                   std::set<std::string> *out_keys)
-{
-  bool first = true;
-  string start_after = orig_start_after;
-  bool more = true;
-  while (max_return > 0 && more) {
-    std::set<std::string> out;
-    ObjectReadOperation op;
-    op.omap_get_keys2(start_after, max_return, &out, &more, nullptr);
-    bufferlist bl;
-    int ret = operate(oid, &op, &bl);
-    if (ret < 0) {
-      return ret;
-    }
-    if (more) {
-      if (out.empty()) {
-       return -EINVAL;  // wth
-      }
-      start_after = *out.rbegin();
-    }
-    if (out.size() <= max_return) {
-      max_return -= out.size();
-    } else {
-      max_return = 0;
-    }
-    if (first) {
-      out_keys->swap(out);
-      first = false;
-    } else {
-      out_keys->insert(out.begin(), out.end());
-      out.clear();
-    }
-  }
-  return 0;
-}
-
-int librados::IoCtx::omap_get_keys2(
-  const std::string& oid,
-  const std::string& start_after,
-  uint64_t max_return,
-  std::set<std::string> *out_keys,
-  bool *pmore)
-{
-  ObjectReadOperation op;
-  int r;
-  op.omap_get_keys2(start_after, max_return, out_keys, pmore, &r);
-  bufferlist bl;
-  int ret = operate(oid, &op, &bl);
-  if (ret < 0)
-    return ret;
-  return r;
-}
-
-int librados::IoCtx::omap_get_header(const std::string& oid,
-                                     bufferlist *bl)
-{
-  ObjectReadOperation op;
-  int r;
-  op.omap_get_header(bl, &r);
-  bufferlist b;
-  int ret = operate(oid, &op, &b);
-  if (ret < 0)
-    return ret;
-
-  return r;
-}
-
-int librados::IoCtx::omap_get_vals_by_keys(const std::string& oid,
-                                           const std::set<std::string>& keys,
-                                           std::map<std::string, bufferlist> *vals)
-{
-  ObjectReadOperation op;
-  int r;
-  bufferlist bl;
-  op.omap_get_vals_by_keys(keys, vals, &r);
-  int ret = operate(oid, &op, &bl);
-  if (ret < 0)
-    return ret;
-
-  return r;
-}
-
-int librados::IoCtx::omap_set(const std::string& oid,
-                              const map<string, bufferlist>& m)
-{
-  ObjectWriteOperation op;
-  op.omap_set(m);
-  return operate(oid, &op);
-}
-
-int librados::IoCtx::omap_set_header(const std::string& oid,
-                                     const bufferlist& bl)
-{
-  ObjectWriteOperation op;
-  op.omap_set_header(bl);
-  return operate(oid, &op);
-}
-
-int librados::IoCtx::omap_clear(const std::string& oid)
-{
-  ObjectWriteOperation op;
-  op.omap_clear();
-  return operate(oid, &op);
-}
-
-int librados::IoCtx::omap_rm_keys(const std::string& oid,
-                                  const std::set<std::string>& keys)
-{
-  ObjectWriteOperation op;
-  op.omap_rm_keys(keys);
-  return operate(oid, &op);
-}
-
-
-
-static int translate_flags(int flags)
-{
-  int op_flags = 0;
-  if (flags & librados::OPERATION_BALANCE_READS)
-    op_flags |= CEPH_OSD_FLAG_BALANCE_READS;
-  if (flags & librados::OPERATION_LOCALIZE_READS)
-    op_flags |= CEPH_OSD_FLAG_LOCALIZE_READS;
-  if (flags & librados::OPERATION_ORDER_READS_WRITES)
-    op_flags |= CEPH_OSD_FLAG_RWORDERED;
-  if (flags & librados::OPERATION_IGNORE_CACHE)
-    op_flags |= CEPH_OSD_FLAG_IGNORE_CACHE;
-  if (flags & librados::OPERATION_SKIPRWLOCKS)
-    op_flags |= CEPH_OSD_FLAG_SKIPRWLOCKS;
-  if (flags & librados::OPERATION_IGNORE_OVERLAY)
-    op_flags |= CEPH_OSD_FLAG_IGNORE_OVERLAY;
-  if (flags & librados::OPERATION_FULL_TRY)
-    op_flags |= CEPH_OSD_FLAG_FULL_TRY;
-  if (flags & librados::OPERATION_FULL_FORCE)
-    op_flags |= CEPH_OSD_FLAG_FULL_FORCE;
-  if (flags & librados::OPERATION_IGNORE_REDIRECT)
-    op_flags |= CEPH_OSD_FLAG_IGNORE_REDIRECT;
-  if (flags & librados::OPERATION_ORDERSNAP)
-    op_flags |= CEPH_OSD_FLAG_ORDERSNAP;
-
-  return op_flags;
-}
-
-int librados::IoCtx::operate(const std::string& oid, librados::ObjectWriteOperation *o)
-{
-  object_t obj(oid);
-  return io_ctx_impl->operate(obj, &o->impl->o, (ceph::real_time *)o->impl->prt);
-}
-
-int librados::IoCtx::operate(const std::string& oid, librados::ObjectReadOperation *o, bufferlist *pbl)
-{
-  object_t obj(oid);
-  return io_ctx_impl->operate_read(obj, &o->impl->o, pbl);
-}
-
-int librados::IoCtx::aio_operate(const std::string& oid, AioCompletion *c,
-                                librados::ObjectWriteOperation *o)
-{
-  object_t obj(oid);
-  return io_ctx_impl->aio_operate(obj, &o->impl->o, c->pc,
-                                 io_ctx_impl->snapc, 0);
-}
-int librados::IoCtx::aio_operate(const std::string& oid, AioCompletion *c,
-                                ObjectWriteOperation *o, int flags)
-{
-  object_t obj(oid);
-  return io_ctx_impl->aio_operate(obj, &o->impl->o, c->pc,
-                                 io_ctx_impl->snapc,
-                                 translate_flags(flags));
-}
-
-int librados::IoCtx::aio_operate(const std::string& oid, AioCompletion *c,
-                                librados::ObjectWriteOperation *o,
-                                snap_t snap_seq, std::vector<snap_t>& snaps)
-{
-  object_t obj(oid);
-  vector<snapid_t> snv;
-  snv.resize(snaps.size());
-  for (size_t i = 0; i < snaps.size(); ++i)
-    snv[i] = snaps[i];
-  SnapContext snapc(snap_seq, snv);
-  return io_ctx_impl->aio_operate(obj, &o->impl->o, c->pc,
-                                 snapc, 0);
-}
-
-int librados::IoCtx::aio_operate(const std::string& oid, AioCompletion *c,
-         librados::ObjectWriteOperation *o,
-         snap_t snap_seq, std::vector<snap_t>& snaps,
-         const blkin_trace_info *trace_info)
-{
-  object_t obj(oid);
-  vector<snapid_t> snv;
-  snv.resize(snaps.size());
-  for (size_t i = 0; i < snaps.size(); ++i)
-    snv[i] = snaps[i];
-  SnapContext snapc(snap_seq, snv);
-  return io_ctx_impl->aio_operate(obj, &o->impl->o, c->pc,
-          snapc, 0, trace_info);
-}
-
-int librados::IoCtx::aio_operate(const std::string& oid, AioCompletion *c,
-         librados::ObjectWriteOperation *o,
-         snap_t snap_seq, std::vector<snap_t>& snaps, int flags,
-         const blkin_trace_info *trace_info)
-{
-  object_t obj(oid);
-  vector<snapid_t> snv;
-  snv.resize(snaps.size());
-  for (size_t i = 0; i < snaps.size(); ++i)
-    snv[i] = snaps[i];
-  SnapContext snapc(snap_seq, snv);
-  return io_ctx_impl->aio_operate(obj, &o->impl->o, c->pc, snapc,
-                                  translate_flags(flags), trace_info);
-}
-
-int librados::IoCtx::aio_operate(const std::string& oid, AioCompletion *c,
-                                librados::ObjectReadOperation *o,
-                                bufferlist *pbl)
-{
-  object_t obj(oid);
-  return io_ctx_impl->aio_operate_read(obj, &o->impl->o, c->pc,
-                                      0, pbl);
-}
-
-// deprecated
-int librados::IoCtx::aio_operate(const std::string& oid, AioCompletion *c,
-                                librados::ObjectReadOperation *o, 
-                                snap_t snapid_unused_deprecated,
-                                int flags, bufferlist *pbl)
-{
-  object_t obj(oid);
-  int op_flags = 0;
-  if (flags & OPERATION_BALANCE_READS)
-    op_flags |= CEPH_OSD_FLAG_BALANCE_READS;
-  if (flags & OPERATION_LOCALIZE_READS)
-    op_flags |= CEPH_OSD_FLAG_LOCALIZE_READS;
-  if (flags & OPERATION_ORDER_READS_WRITES)
-    op_flags |= CEPH_OSD_FLAG_RWORDERED;
-
-  return io_ctx_impl->aio_operate_read(obj, &o->impl->o, c->pc,
-                                      op_flags, pbl);
-}
-
-int librados::IoCtx::aio_operate(const std::string& oid, AioCompletion *c,
-                                librados::ObjectReadOperation *o,
-                                int flags, bufferlist *pbl)
-{
-  object_t obj(oid);
-  return io_ctx_impl->aio_operate_read(obj, &o->impl->o, c->pc,
-                                      translate_flags(flags), pbl);
-}
-
-int librados::IoCtx::aio_operate(const std::string& oid, AioCompletion *c,
-         librados::ObjectReadOperation *o,
-         int flags, bufferlist *pbl, const blkin_trace_info *trace_info)
-{
-  object_t obj(oid);
-  return io_ctx_impl->aio_operate_read(obj, &o->impl->o, c->pc,
-               translate_flags(flags), pbl, trace_info);
-}
-
-void librados::IoCtx::snap_set_read(snap_t seq)
-{
-  io_ctx_impl->set_snap_read(seq);
-}
-
-int librados::IoCtx::selfmanaged_snap_set_write_ctx(snap_t seq, vector<snap_t>& snaps)
-{
-  vector<snapid_t> snv;
-  snv.resize(snaps.size());
-  for (unsigned i=0; i<snaps.size(); i++)
-    snv[i] = snaps[i];
-  return io_ctx_impl->set_snap_write_context(seq, snv);
-}
-
-int librados::IoCtx::snap_create(const char *snapname)
-{
-  return io_ctx_impl->snap_create(snapname);
-}
-
-int librados::IoCtx::snap_lookup(const char *name, snap_t *snapid)
-{
-  return io_ctx_impl->snap_lookup(name, snapid);
-}
-
-int librados::IoCtx::snap_get_stamp(snap_t snapid, time_t *t)
-{
-  return io_ctx_impl->snap_get_stamp(snapid, t);
-}
-
-int librados::IoCtx::snap_get_name(snap_t snapid, std::string *s)
-{
-  return io_ctx_impl->snap_get_name(snapid, s);
-}
-
-int librados::IoCtx::snap_remove(const char *snapname)
-{
-  return io_ctx_impl->snap_remove(snapname);
-}
-
-int librados::IoCtx::snap_list(std::vector<snap_t> *snaps)
-{
-  return io_ctx_impl->snap_list(snaps);
-}
-
-int librados::IoCtx::snap_rollback(const std::string& oid, const char *snapname)
-{
-  return io_ctx_impl->rollback(oid, snapname);
-}
-
-// Deprecated name kept for backward compatibility
-int librados::IoCtx::rollback(const std::string& oid, const char *snapname)
-{
-  return snap_rollback(oid, snapname);
-}
-
-int librados::IoCtx::selfmanaged_snap_create(uint64_t *snapid)
-{
-  return io_ctx_impl->selfmanaged_snap_create(snapid);
-}
-
-void librados::IoCtx::aio_selfmanaged_snap_create(uint64_t *snapid,
-                                                  AioCompletion *c)
-{
-  io_ctx_impl->aio_selfmanaged_snap_create(snapid, c->pc);
-}
-
-int librados::IoCtx::selfmanaged_snap_remove(uint64_t snapid)
-{
-  return io_ctx_impl->selfmanaged_snap_remove(snapid);
-}
-
-void librados::IoCtx::aio_selfmanaged_snap_remove(uint64_t snapid,
-                                                  AioCompletion *c)
-{
-  io_ctx_impl->aio_selfmanaged_snap_remove(snapid, c->pc);
-}
-
-int librados::IoCtx::selfmanaged_snap_rollback(const std::string& oid, uint64_t snapid)
-{
-  return io_ctx_impl->selfmanaged_snap_rollback_object(oid,
-                                                      io_ctx_impl->snapc,
-                                                      snapid);
-}
-
-int librados::IoCtx::lock_exclusive(const std::string &oid, const std::string &name,
-                                   const std::string &cookie,
-                                   const std::string &description,
-                                   struct timeval * duration, uint8_t flags)
-{
-  utime_t dur = utime_t();
-  if (duration)
-    dur.set_from_timeval(duration);
-
-  return rados::cls::lock::lock(this, oid, name, LOCK_EXCLUSIVE, cookie, "",
-                               description, dur, flags);
-}
-
-int librados::IoCtx::lock_shared(const std::string &oid, const std::string &name,
-                                const std::string &cookie, const std::string &tag,
-                                const std::string &description,
-                                struct timeval * duration, uint8_t flags)
-{
-  utime_t dur = utime_t();
-  if (duration)
-    dur.set_from_timeval(duration);
-
-  return rados::cls::lock::lock(this, oid, name, LOCK_SHARED, cookie, tag,
-                               description, dur, flags);
-}
-
-int librados::IoCtx::unlock(const std::string &oid, const std::string &name,
-                           const std::string &cookie)
-{
-  return rados::cls::lock::unlock(this, oid, name, cookie);
-}
-
-struct AioUnlockCompletion : public librados::ObjectOperationCompletion {
-  librados::AioCompletionImpl *completion;
-  AioUnlockCompletion(librados::AioCompletion *c) : completion(c->pc) {
-    completion->get();
-  };
-  void handle_completion(int r, bufferlist& outbl) override {
-    rados_callback_t cb = completion->callback_complete;
-    void *cb_arg = completion->callback_complete_arg;
-    cb(completion, cb_arg);
-    completion->lock.Lock();
-    completion->callback_complete = NULL;
-    completion->cond.Signal();
-    completion->put_unlock();
-  }
-};
-
-int librados::IoCtx::aio_unlock(const std::string &oid, const std::string &name,
-                               const std::string &cookie, AioCompletion *c)
-{
-  return rados::cls::lock::aio_unlock(this, oid, name, cookie, c);
-}
-
-int librados::IoCtx::break_lock(const std::string &oid, const std::string &name,
-                               const std::string &client, const std::string &cookie)
-{
-  entity_name_t locker;
-  if (!locker.parse(client))
-    return -EINVAL;
-  return rados::cls::lock::break_lock(this, oid, name, cookie, locker);
-}
-
-int librados::IoCtx::list_lockers(const std::string &oid, const std::string &name,
-                                 int *exclusive,
-                                 std::string *tag,
-                                 std::list<librados::locker_t> *lockers)
-{
-  std::list<librados::locker_t> tmp_lockers;
-  map<rados::cls::lock::locker_id_t, rados::cls::lock::locker_info_t> rados_lockers;
-  std::string tmp_tag;
-  ClsLockType tmp_type;
-  int r = rados::cls::lock::get_lock_info(this, oid, name, &rados_lockers, &tmp_type, &tmp_tag);
-  if (r < 0)
-         return r;
-
-  map<rados::cls::lock::locker_id_t, rados::cls::lock::locker_info_t>::iterator map_it;
-  for (map_it = rados_lockers.begin(); map_it != rados_lockers.end(); ++map_it) {
-    librados::locker_t locker;
-    locker.client = stringify(map_it->first.locker);
-    locker.cookie = map_it->first.cookie;
-    locker.address = stringify(map_it->second.addr);
-    tmp_lockers.push_back(locker);
-  }
-
-  if (lockers)
-    *lockers = tmp_lockers;
-  if (tag)
-    *tag = tmp_tag;
-  if (exclusive) {
-    if (tmp_type == LOCK_EXCLUSIVE)
-      *exclusive = 1;
-    else
-      *exclusive = 0;
-  }
-
-  return tmp_lockers.size();
-}
-
-librados::NObjectIterator librados::IoCtx::nobjects_begin()
-{
-  bufferlist bl;
-  return nobjects_begin(bl);
-}
-
-librados::NObjectIterator librados::IoCtx::nobjects_begin(
-    const bufferlist &filter)
-{
-  rados_list_ctx_t listh;
-  rados_nobjects_list_open(io_ctx_impl, &listh);
-  NObjectIterator iter((ObjListCtx*)listh);
-  if (filter.length() > 0) {
-    iter.set_filter(filter);
-  }
-  iter.get_next();
-  return iter;
-}
-
-librados::NObjectIterator librados::IoCtx::nobjects_begin(uint32_t pos)
-{
-  bufferlist bl;
-  return nobjects_begin(pos, bl);
-}
-
-librados::NObjectIterator librados::IoCtx::nobjects_begin(
-  uint32_t pos, const bufferlist &filter)
-{
-  rados_list_ctx_t listh;
-  rados_nobjects_list_open(io_ctx_impl, &listh);
-  NObjectIterator iter((ObjListCtx*)listh);
-  if (filter.length() > 0) {
-    iter.set_filter(filter);
-  }
-  iter.seek(pos);
-  return iter;
-}
-
-librados::NObjectIterator librados::IoCtx::nobjects_begin(const ObjectCursor& cursor)
-{
-  bufferlist bl;
-  return nobjects_begin(cursor, bl);
-}
-
-librados::NObjectIterator librados::IoCtx::nobjects_begin(
-  const ObjectCursor& cursor, const bufferlist &filter)
-{
-  rados_list_ctx_t listh;
-  rados_nobjects_list_open(io_ctx_impl, &listh);
-  NObjectIterator iter((ObjListCtx*)listh);
-  if (filter.length() > 0) {
-    iter.set_filter(filter);
-  }
-  iter.seek(cursor);
-  return iter;
-}
-
-const librados::NObjectIterator& librados::IoCtx::nobjects_end() const
-{
-  return NObjectIterator::__EndObjectIterator;
-}
-
-int librados::IoCtx::hit_set_list(uint32_t hash, AioCompletion *c,
-                                 std::list< std::pair<time_t, time_t> > *pls)
-{
-  return io_ctx_impl->hit_set_list(hash, c->pc, pls);
-}
-
-int librados::IoCtx::hit_set_get(uint32_t hash,  AioCompletion *c, time_t stamp,
-                                bufferlist *pbl)
-{
-  return io_ctx_impl->hit_set_get(hash, c->pc, stamp, pbl);
-}
-
-
-
-uint64_t librados::IoCtx::get_last_version()
-{
-  return io_ctx_impl->last_version();
-}
-
-int librados::IoCtx::aio_read(const std::string& oid, librados::AioCompletion *c,
-                             bufferlist *pbl, size_t len, uint64_t off)
-{
-  return io_ctx_impl->aio_read(oid, c->pc, pbl, len, off,
-                              io_ctx_impl->snap_seq);
-}
-
-int librados::IoCtx::aio_read(const std::string& oid, librados::AioCompletion *c,
-                             bufferlist *pbl, size_t len, uint64_t off,
-                             uint64_t snapid)
-{
-  return io_ctx_impl->aio_read(oid, c->pc, pbl, len, off, snapid);
-}
-
-int librados::IoCtx::aio_exec(const std::string& oid,
-                             librados::AioCompletion *c, const char *cls,
-                             const char *method, bufferlist& inbl,
-                             bufferlist *outbl)
-{
-  object_t obj(oid);
-  return io_ctx_impl->aio_exec(obj, c->pc, cls, method, inbl, outbl);
-}
-
-int librados::IoCtx::aio_cmpext(const std::string& oid,
-                               librados::AioCompletion *c,
-                               uint64_t off,
-                               bufferlist& cmp_bl)
-{
-  return io_ctx_impl->aio_cmpext(oid, c->pc, off, cmp_bl);
-}
-
-int librados::IoCtx::aio_sparse_read(const std::string& oid, librados::AioCompletion *c,
-                                    std::map<uint64_t,uint64_t> *m, bufferlist *data_bl,
-                                    size_t len, uint64_t off)
-{
-  return io_ctx_impl->aio_sparse_read(oid, c->pc,
-                                     m, data_bl, len, off,
-                                     io_ctx_impl->snap_seq);
-}
-
-int librados::IoCtx::aio_sparse_read(const std::string& oid, librados::AioCompletion *c,
-                                    std::map<uint64_t,uint64_t> *m, bufferlist *data_bl,
-                                    size_t len, uint64_t off, uint64_t snapid)
-{
-  return io_ctx_impl->aio_sparse_read(oid, c->pc,
-                                     m, data_bl, len, off, snapid);
-}
-
-int librados::IoCtx::aio_write(const std::string& oid, librados::AioCompletion *c,
-                              const bufferlist& bl, size_t len, uint64_t off)
-{
-  return io_ctx_impl->aio_write(oid, c->pc, bl, len, off);
-}
-
-int librados::IoCtx::aio_append(const std::string& oid, librados::AioCompletion *c,
-                               const bufferlist& bl, size_t len)
-{
-  return io_ctx_impl->aio_append(oid, c->pc, bl, len);
-}
-
-int librados::IoCtx::aio_write_full(const std::string& oid, librados::AioCompletion *c,
-                                   const bufferlist& bl)
-{
-  object_t obj(oid);
-  return io_ctx_impl->aio_write_full(obj, c->pc, bl);
-}
-
-int librados::IoCtx::aio_writesame(const std::string& oid, librados::AioCompletion *c,
-                                  const bufferlist& bl, size_t write_len,
-                                  uint64_t off)
-{
-  return io_ctx_impl->aio_writesame(oid, c->pc, bl, write_len, off);
-}
-
-
-int librados::IoCtx::aio_remove(const std::string& oid, librados::AioCompletion *c)
-{
-  return io_ctx_impl->aio_remove(oid, c->pc);
-}
-
-int librados::IoCtx::aio_remove(const std::string& oid, librados::AioCompletion *c, int flags)
-{
-  return io_ctx_impl->aio_remove(oid, c->pc, flags);
-}
-
-int librados::IoCtx::aio_flush_async(librados::AioCompletion *c)
-{
-  io_ctx_impl->flush_aio_writes_async(c->pc);
-  return 0;
-}
-
-int librados::IoCtx::aio_flush()
-{
-  io_ctx_impl->flush_aio_writes();
-  return 0;
-}
-
-struct AioGetxattrDataPP {
-  AioGetxattrDataPP(librados::AioCompletionImpl *c, bufferlist *_bl) :
-    bl(_bl), completion(c) {}
-  bufferlist *bl;
-  struct librados::C_AioCompleteAndSafe completion;
-};
-
-static void rados_aio_getxattr_completepp(rados_completion_t c, void *arg) {
-  AioGetxattrDataPP *cdata = reinterpret_cast<AioGetxattrDataPP*>(arg);
-  int rc = rados_aio_get_return_value(c);
-  if (rc >= 0) {
-    rc = cdata->bl->length();
-  }
-  cdata->completion.finish(rc);
-  delete cdata;
-}
-
-int librados::IoCtx::aio_getxattr(const std::string& oid, librados::AioCompletion *c,
-                                 const char *name, bufferlist& bl)
-{
-  // create data object to be passed to async callback
-  AioGetxattrDataPP *cdata = new AioGetxattrDataPP(c->pc, &bl);
-  if (!cdata) {
-    return -ENOMEM;
-  }
-  // create completion callback
-  librados::AioCompletionImpl *comp = new librados::AioCompletionImpl;
-  comp->set_complete_callback(cdata, rados_aio_getxattr_completepp);
-  // call actual getxattr from IoCtxImpl
-  object_t obj(oid);
-  return io_ctx_impl->aio_getxattr(obj, comp, name, bl);
-}
-
-int librados::IoCtx::aio_getxattrs(const std::string& oid, AioCompletion *c,
-                                  map<std::string, bufferlist>& attrset)
-{
-  object_t obj(oid);
-  return io_ctx_impl->aio_getxattrs(obj, c->pc, attrset);
-}
-
-int librados::IoCtx::aio_setxattr(const std::string& oid, AioCompletion *c,
-                                 const char *name, bufferlist& bl)
-{
-  object_t obj(oid);
-  return io_ctx_impl->aio_setxattr(obj, c->pc, name, bl);
-}
-
-int librados::IoCtx::aio_rmxattr(const std::string& oid, AioCompletion *c,
-                                const char *name)
-{
-  object_t obj(oid);
-  return io_ctx_impl->aio_rmxattr(obj, c->pc, name);
-}
-
-int librados::IoCtx::aio_stat(const std::string& oid, librados::AioCompletion *c,
-                             uint64_t *psize, time_t *pmtime)
-{
-  object_t obj(oid);
-  return io_ctx_impl->aio_stat(obj, c->pc, psize, pmtime);
-}
-
-int librados::IoCtx::aio_cancel(librados::AioCompletion *c)
-{
-  return io_ctx_impl->aio_cancel(c->pc);
-}
-
-int librados::IoCtx::watch(const string& oid, uint64_t ver, uint64_t *cookie,
-                          librados::WatchCtx *ctx)
-{
-  object_t obj(oid);
-  return io_ctx_impl->watch(obj, cookie, ctx, NULL);
-}
-
-int librados::IoCtx::watch2(const string& oid, uint64_t *cookie,
-                           librados::WatchCtx2 *ctx2)
-{
-  object_t obj(oid);
-  return io_ctx_impl->watch(obj, cookie, NULL, ctx2);
-}
-
-int librados::IoCtx::watch3(const string& oid, uint64_t *cookie,
-          librados::WatchCtx2 *ctx2, uint32_t timeout)
-{
-  object_t obj(oid);
-  return io_ctx_impl->watch(obj, cookie, NULL, ctx2, timeout);
-}
-
-int librados::IoCtx::aio_watch(const string& oid, AioCompletion *c,
-                               uint64_t *cookie,
-                               librados::WatchCtx2 *ctx2)
-{
-  object_t obj(oid);
-  return io_ctx_impl->aio_watch(obj, c->pc, cookie, NULL, ctx2);
-}
-
-int librados::IoCtx::aio_watch2(const string& oid, AioCompletion *c,
-                                uint64_t *cookie,
-                                librados::WatchCtx2 *ctx2,
-                                uint32_t timeout)
-{
-  object_t obj(oid);
-  return io_ctx_impl->aio_watch(obj, c->pc, cookie, NULL, ctx2, timeout);
-}
-
-int librados::IoCtx::unwatch(const string& oid, uint64_t handle)
-{
-  return io_ctx_impl->unwatch(handle);
-}
-
-int librados::IoCtx::unwatch2(uint64_t handle)
-{
-  return io_ctx_impl->unwatch(handle);
-}
-
-int librados::IoCtx::aio_unwatch(uint64_t handle, AioCompletion *c)
-{
-  return io_ctx_impl->aio_unwatch(handle, c->pc);
-}
-
-int librados::IoCtx::watch_check(uint64_t handle)
-{
-  return io_ctx_impl->watch_check(handle);
-}
-
-int librados::IoCtx::notify(const string& oid, uint64_t ver, bufferlist& bl)
-{
-  object_t obj(oid);
-  return io_ctx_impl->notify(obj, bl, 0, NULL, NULL, NULL);
-}
-
-int librados::IoCtx::notify2(const string& oid, bufferlist& bl,
-                            uint64_t timeout_ms, bufferlist *preplybl)
-{
-  object_t obj(oid);
-  return io_ctx_impl->notify(obj, bl, timeout_ms, preplybl, NULL, NULL);
-}
-
-int librados::IoCtx::aio_notify(const string& oid, AioCompletion *c,
-                                bufferlist& bl, uint64_t timeout_ms,
-                                bufferlist *preplybl)
-{
-  object_t obj(oid);
-  return io_ctx_impl->aio_notify(obj, c->pc, bl, timeout_ms, preplybl, NULL,
-                                 NULL);
-}
-
-void librados::IoCtx::notify_ack(const std::string& o,
-                                uint64_t notify_id, uint64_t handle,
-                                bufferlist& bl)
-{
-  io_ctx_impl->notify_ack(o, notify_id, handle, bl);
-}
-
-int librados::IoCtx::list_watchers(const std::string& oid,
-                                   std::list<obj_watch_t> *out_watchers)
-{
-  ObjectReadOperation op;
-  int r;
-  op.list_watchers(out_watchers, &r);
-  bufferlist bl;
-  int ret = operate(oid, &op, &bl);
-  if (ret < 0)
-    return ret;
-
-  return r;
-}
-
-int librados::IoCtx::list_snaps(const std::string& oid,
-                                   snap_set_t *out_snaps)
-{
-  ObjectReadOperation op;
-  int r;
-  if (io_ctx_impl->snap_seq != CEPH_SNAPDIR)
-    return -EINVAL;
-  op.list_snaps(out_snaps, &r);
-  bufferlist bl;
-  int ret = operate(oid, &op, &bl);
-  if (ret < 0)
-    return ret;
-
-  return r;
-}
-
-void librados::IoCtx::set_notify_timeout(uint32_t timeout)
-{
-  io_ctx_impl->set_notify_timeout(timeout);
-}
-
-int librados::IoCtx::set_alloc_hint(const std::string& o,
-                                    uint64_t expected_object_size,
-                                    uint64_t expected_write_size)
-{
-  object_t oid(o);
-  return io_ctx_impl->set_alloc_hint(oid, expected_object_size,
-                                     expected_write_size, 0);
-}
-
-int librados::IoCtx::set_alloc_hint2(const std::string& o,
-                                    uint64_t expected_object_size,
-                                    uint64_t expected_write_size,
-                                    uint32_t flags)
-{
-  object_t oid(o);
-  return io_ctx_impl->set_alloc_hint(oid, expected_object_size,
-                                     expected_write_size, flags);
-}
-
-void librados::IoCtx::set_assert_version(uint64_t ver)
-{
-  io_ctx_impl->set_assert_version(ver);
-}
-
-void librados::IoCtx::locator_set_key(const string& key)
-{
-  io_ctx_impl->oloc.key = key;
-}
-
-void librados::IoCtx::set_namespace(const string& nspace)
-{
-  io_ctx_impl->oloc.nspace = nspace;
-}
-
-std::string librados::IoCtx::get_namespace() const
-{
-  return io_ctx_impl->oloc.nspace;
-}
-
-int64_t librados::IoCtx::get_id()
-{
-  return io_ctx_impl->get_id();
-}
-
-uint32_t librados::IoCtx::get_object_hash_position(const std::string& oid)
-{
-  uint32_t hash;
-  int r = io_ctx_impl->get_object_hash_position(oid, &hash);
-  if (r < 0)
-    hash = 0;
-  return hash;
-}
-
-uint32_t librados::IoCtx::get_object_pg_hash_position(const std::string& oid)
-{
-  uint32_t hash;
-  int r = io_ctx_impl->get_object_pg_hash_position(oid, &hash);
-  if (r < 0)
-    hash = 0;
-  return hash;
-}
-
-int librados::IoCtx::get_object_hash_position2(
-    const std::string& oid, uint32_t *hash_position)
-{
-  return io_ctx_impl->get_object_hash_position(oid, hash_position);
-}
-
-int librados::IoCtx::get_object_pg_hash_position2(
-    const std::string& oid, uint32_t *pg_hash_position)
-{
-  return io_ctx_impl->get_object_pg_hash_position(oid, pg_hash_position);
-}
-
-librados::config_t librados::IoCtx::cct()
-{
-  return (config_t)io_ctx_impl->client->cct;
-}
-
-librados::IoCtx::IoCtx(IoCtxImpl *io_ctx_impl_)
-  : io_ctx_impl(io_ctx_impl_)
-{
-}
-
-void librados::IoCtx::set_osdmap_full_try()
-{
-  io_ctx_impl->objecter->set_osdmap_full_try();
-}
-
-void librados::IoCtx::unset_osdmap_full_try()
-{
-  io_ctx_impl->objecter->unset_osdmap_full_try();
-}
-
-///////////////////////////// Rados //////////////////////////////
-void librados::Rados::version(int *major, int *minor, int *extra)
-{
-  rados_version(major, minor, extra);
-}
-
-librados::Rados::Rados() : client(NULL)
-{
-}
-
-librados::Rados::Rados(IoCtx &ioctx)
-{
-  client = ioctx.io_ctx_impl->client;
-  ceph_assert(client != NULL);
-  client->get();
-}
-
-librados::Rados::~Rados()
-{
-  shutdown();
-}
-
-int librados::Rados::init(const char * const id)
-{
-  return rados_create((rados_t *)&client, id);
-}
-
-int librados::Rados::init2(const char * const name,
-                          const char * const clustername, uint64_t flags)
-{
-  return rados_create2((rados_t *)&client, clustername, name, flags);
-}
-
-int librados::Rados::init_with_context(config_t cct_)
-{
-  return rados_create_with_context((rados_t *)&client, (rados_config_t)cct_);
-}
-
-int librados::Rados::connect()
-{
-  return client->connect();
-}
-
-librados::config_t librados::Rados::cct()
-{
-  return (config_t)client->cct;
-}
-
-int librados::Rados::watch_flush()
-{
-  if (!client)
-    return -EINVAL;
-  return client->watch_flush();
-}
-
-int librados::Rados::aio_watch_flush(AioCompletion *c)
-{
-  if (!client)
-    return -EINVAL;
-  return client->async_watch_flush(c->pc);
-}
-
-void librados::Rados::shutdown()
-{
-  if (!client)
-    return;
-  if (client->put()) {
-    client->shutdown();
-    delete client;
-    client = NULL;
-  }
-}
-
-uint64_t librados::Rados::get_instance_id()
-{
-  return client->get_instance_id();
-}
-
-int librados::Rados::get_min_compatible_osd(int8_t* require_osd_release)
-{
-  return client->get_min_compatible_osd(require_osd_release);
-}
-
-int librados::Rados::get_min_compatible_client(int8_t* min_compat_client,
-                                               int8_t* require_min_compat_client)
-{
-  return client->get_min_compatible_client(min_compat_client,
-                                           require_min_compat_client);
-}
-
-int librados::Rados::conf_read_file(const char * const path) const
-{
-  return rados_conf_read_file((rados_t)client, path);
-}
-
-int librados::Rados::conf_parse_argv(int argc, const char ** argv) const
-{
-  return rados_conf_parse_argv((rados_t)client, argc, argv);
-}
-
-int librados::Rados::conf_parse_argv_remainder(int argc, const char ** argv,
-                                              const char ** remargv) const
-{
-  return rados_conf_parse_argv_remainder((rados_t)client, argc, argv, remargv);
-}
-
-int librados::Rados::conf_parse_env(const char *name) const
-{
-  return rados_conf_parse_env((rados_t)client, name);
-}
-
-int librados::Rados::conf_set(const char *option, const char *value)
-{
-  return rados_conf_set((rados_t)client, option, value);
-}
-
-int librados::Rados::conf_get(const char *option, std::string &val)
-{
-  char *str = NULL;
-  const auto& conf = client->cct->_conf;
-  int ret = conf.get_val(option, &str, -1);
-  if (ret) {
-    free(str);
-    return ret;
-  }
-  val = str;
-  free(str);
-  return 0;
-}
-
-int librados::Rados::service_daemon_register(
-  const std::string& service,  ///< service name (e.g., 'rgw')
-  const std::string& name,     ///< daemon name (e.g., 'gwfoo')
-  const std::map<std::string,std::string>& metadata) ///< static metadata about daemon
-{
-  return client->service_daemon_register(service, name, metadata);
-}
-
-int librados::Rados::service_daemon_update_status(
-  std::map<std::string,std::string>&& status)
-{
-  return client->service_daemon_update_status(std::move(status));
-}
-
-int librados::Rados::pool_create(const char *name)
-{
-  string str(name);
-  return client->pool_create(str);
-}
-
-int librados::Rados::pool_create(const char *name, uint64_t auid)
-{
-  if (auid != CEPH_AUTH_UID_DEFAULT) {
-    return -EINVAL;
-  }
-  string str(name);
-  return client->pool_create(str);
-}
-
-int librados::Rados::pool_create(const char *name, uint64_t auid, __u8 crush_rule)
-{
-  if (auid != CEPH_AUTH_UID_DEFAULT) {
-    return -EINVAL;
-  }
-  string str(name);
-  return client->pool_create(str, crush_rule);
-}
-
-int librados::Rados::pool_create_with_rule(const char *name, __u8 crush_rule)
-{
-  string str(name);
-  return client->pool_create(str, crush_rule);
-}
-
-int librados::Rados::pool_create_async(const char *name, PoolAsyncCompletion *c)
-{
-  string str(name);
-  return client->pool_create_async(str, c->pc);
-}
-
-int librados::Rados::pool_create_async(const char *name, uint64_t auid, PoolAsyncCompletion *c)
-{
-  if (auid != CEPH_AUTH_UID_DEFAULT) {
-    return -EINVAL;
-  }
-  string str(name);
-  return client->pool_create_async(str, c->pc);
-}
-
-int librados::Rados::pool_create_async(const char *name, uint64_t auid, __u8 crush_rule,
-                                      PoolAsyncCompletion *c)
-{
-  if (auid != CEPH_AUTH_UID_DEFAULT) {
-    return -EINVAL;
-  }
-  string str(name);
-  return client->pool_create_async(str, c->pc, crush_rule);
-}
-
-int librados::Rados::pool_create_with_rule_async(
-  const char *name, __u8 crush_rule,
-  PoolAsyncCompletion *c)
-{
-  string str(name);
-  return client->pool_create_async(str, c->pc, crush_rule);
-}
-
-int librados::Rados::pool_get_base_tier(int64_t pool_id, int64_t* base_tier)
-{
-  tracepoint(librados, rados_pool_get_base_tier_enter, (rados_t)client, pool_id);
-  int retval = client->pool_get_base_tier(pool_id, base_tier);
-  tracepoint(librados, rados_pool_get_base_tier_exit, retval, *base_tier);
-  return retval;
-}
-
-int librados::Rados::pool_delete(const char *name)
-{
-  return client->pool_delete(name);
-}
-
-int librados::Rados::pool_delete_async(const char *name, PoolAsyncCompletion *c)
-{
-  return client->pool_delete_async(name, c->pc);
-}
-
-int librados::Rados::pool_list(std::list<std::string>& v)
-{
-  std::list<std::pair<int64_t, std::string> > pools;
-  int r = client->pool_list(pools);
-  if (r < 0) {
-    return r;
-  }
-
-  v.clear();
-  for (std::list<std::pair<int64_t, std::string> >::iterator it = pools.begin();
-       it != pools.end(); ++it) {
-    v.push_back(it->second);
-  }
-  return 0;
-}
-
-int librados::Rados::pool_list2(std::list<std::pair<int64_t, std::string> >& v)
-{
-  return client->pool_list(v);
-}
-
-int64_t librados::Rados::pool_lookup(const char *name)
-{
-  return client->lookup_pool(name);
-}
-
-int librados::Rados::pool_reverse_lookup(int64_t id, std::string *name)
-{
-  return client->pool_get_name(id, name);
-}
-
-int librados::Rados::mon_command(string cmd, const bufferlist& inbl,
-                                bufferlist *outbl, string *outs)
-{
-  vector<string> cmdvec;
-  cmdvec.push_back(cmd);
-  return client->mon_command(cmdvec, inbl, outbl, outs);
-}
-
-int librados::Rados::osd_command(int osdid, std::string cmd, const bufferlist& inbl,
-                                 bufferlist *outbl, std::string *outs)
-{
-  vector<string> cmdvec;
-  cmdvec.push_back(cmd);
-  return client->osd_command(osdid, cmdvec, inbl, outbl, outs);
-}
-
-int librados::Rados::mgr_command(std::string cmd, const bufferlist& inbl,
-                                 bufferlist *outbl, std::string *outs)
-{
-  vector<string> cmdvec;
-  cmdvec.push_back(cmd);
-  return client->mgr_command(cmdvec, inbl, outbl, outs);
-}
-
-
-
-int librados::Rados::pg_command(const char *pgstr, std::string cmd, const bufferlist& inbl,
-                                bufferlist *outbl, std::string *outs)
-{
-  vector<string> cmdvec;
-  cmdvec.push_back(cmd);
-
-  pg_t pgid;
-  if (!pgid.parse(pgstr))
-    return -EINVAL;
-
-  return client->pg_command(pgid, cmdvec, inbl, outbl, outs);
-}
-
-int librados::Rados::ioctx_create(const char *name, IoCtx &io)
-{
-  rados_ioctx_t p;
-  int ret = rados_ioctx_create((rados_t)client, name, &p);
-  if (ret)
-    return ret;
-  io.close();
-  io.io_ctx_impl = (IoCtxImpl*)p;
-  return 0;
-}
-
-int librados::Rados::ioctx_create2(int64_t pool_id, IoCtx &io)
-{
-  rados_ioctx_t p;
-  int ret = rados_ioctx_create2((rados_t)client, pool_id, &p);
-  if (ret)
-    return ret;
-  io.close();
-  io.io_ctx_impl = (IoCtxImpl*)p;
-  return 0;
-}
-
-void librados::Rados::test_blacklist_self(bool set)
-{
-  client->blacklist_self(set);
-}
-
-int librados::Rados::get_pool_stats(std::list<string>& v,
-                                   stats_map& result)
-{
-  map<string,::pool_stat_t> rawresult;
-  int r = client->get_pool_stats(v, rawresult);
-  for (map<string,::pool_stat_t>::iterator p = rawresult.begin();
-       p != rawresult.end();
-       ++p) {
-    pool_stat_t& pv = result[p->first];
-    object_stat_sum_t *sum = &p->second.stats.sum;
-    pv.num_kb = shift_round_up(sum->num_bytes, 10);
-    pv.num_bytes = sum->num_bytes;
-    pv.num_objects = sum->num_objects;
-    pv.num_object_clones = sum->num_object_clones;
-    pv.num_object_copies = sum->num_object_copies;
-    pv.num_objects_missing_on_primary = sum->num_objects_missing_on_primary;
-    pv.num_objects_unfound = sum->num_objects_unfound;
-    pv.num_objects_degraded = sum->num_objects_degraded;
-    pv.num_rd = sum->num_rd;
-    pv.num_rd_kb = sum->num_rd_kb;
-    pv.num_wr = sum->num_wr;
-    pv.num_wr_kb = sum->num_wr_kb;
-  }
-  return r;
-}
-
-int librados::Rados::get_pool_stats(std::list<string>& v,
-                                   std::map<string, stats_map>& result)
-{
-  stats_map m;
-  int r = get_pool_stats(v, m);
-  if (r < 0)
-    return r;
-  for (map<string,pool_stat_t>::iterator p = m.begin();
-       p != m.end();
-       ++p) {
-    result[p->first][string()] = p->second;
-  }
-  return r;
-}
-
-int librados::Rados::get_pool_stats(std::list<string>& v,
-                                   string& category, // unused
-                                   std::map<string, stats_map>& result)
-{
-  return -EOPNOTSUPP;
-}
-
-bool librados::Rados::get_pool_is_selfmanaged_snaps_mode(const std::string& pool)
-{
-  return client->get_pool_is_selfmanaged_snaps_mode(pool);
-}
-
-int librados::Rados::cluster_stat(cluster_stat_t& result)
-{
-  ceph_statfs stats;
-  int r = client->get_fs_stats(stats);
-  result.kb = stats.kb;
-  result.kb_used = stats.kb_used;
-  result.kb_avail = stats.kb_avail;
-  result.num_objects = stats.num_objects;
-  return r;
-}
-
-int librados::Rados::cluster_fsid(string *fsid)
-{
-  return client->get_fsid(fsid);
-}
-
-namespace librados {
-  struct PlacementGroupImpl {
-    pg_t pgid;
-  };
-
-  PlacementGroup::PlacementGroup()
-    : impl{new PlacementGroupImpl}
-  {}
-
-  PlacementGroup::PlacementGroup(const PlacementGroup& pg)
-    : impl{new PlacementGroupImpl}
-  {
-    impl->pgid = pg.impl->pgid;
-  }
-
-  PlacementGroup::~PlacementGroup()
-  {}
-
-  bool PlacementGroup::parse(const char* s)
-  {
-    return impl->pgid.parse(s);
-  }
-}
-
-std::ostream& librados::operator<<(std::ostream& out,
-                                  const librados::PlacementGroup& pg)
-{
-  return out << pg.impl->pgid;
-}
-
-int librados::Rados::get_inconsistent_pgs(int64_t pool_id,
-                                         std::vector<PlacementGroup>* pgs)
-{
-  std::vector<string> pgids;
-  if (auto ret = client->get_inconsistent_pgs(pool_id, &pgids); ret) {
-    return ret;
-  }
-  for (const auto& pgid : pgids) {
-    librados::PlacementGroup pg;
-    if (!pg.parse(pgid.c_str())) {
-      return -EINVAL;
-    }
-    pgs->emplace_back(std::move(pg));
-  }
-  return 0;
-}
-
-int librados::Rados::get_inconsistent_objects(const PlacementGroup& pg,
-                                             const object_id_t &start_after,
-                                             unsigned max_return,
-                                             AioCompletion *c,
-                                             std::vector<inconsistent_obj_t>* objects,
-                                             uint32_t* interval)
-{
-  IoCtx ioctx;
-  const pg_t pgid = pg.impl->pgid;
-  int r = ioctx_create2(pgid.pool(), ioctx);
-  if (r < 0) {
-    return r;
-  }
-
-  return ioctx.io_ctx_impl->get_inconsistent_objects(pgid,
-                                                    start_after,
-                                                    max_return,
-                                                    c->pc,
-                                                    objects,
-                                                    interval);
-}
-
-int librados::Rados::get_inconsistent_snapsets(const PlacementGroup& pg,
-                                              const object_id_t &start_after,
-                                              unsigned max_return,
-                                              AioCompletion *c,
-                                              std::vector<inconsistent_snapset_t>* snapsets,
-                                              uint32_t* interval)
-{
-  IoCtx ioctx;
-  const pg_t pgid = pg.impl->pgid;
-  int r = ioctx_create2(pgid.pool(), ioctx);
-  if (r < 0) {
-    return r;
-  }
-
-  return ioctx.io_ctx_impl->get_inconsistent_snapsets(pgid,
-                                                     start_after,
-                                                     max_return,
-                                                     c->pc,
-                                                     snapsets,
-                                                     interval);
-}
-
-int librados::Rados::wait_for_latest_osdmap()
-{
-  return client->wait_for_latest_osdmap();
-}
-
-int librados::Rados::blacklist_add(const std::string& client_address,
-                                  uint32_t expire_seconds)
-{
-  return client->blacklist_add(client_address, expire_seconds);
-}
-
-librados::PoolAsyncCompletion *librados::Rados::pool_async_create_completion()
-{
-  PoolAsyncCompletionImpl *c = new PoolAsyncCompletionImpl;
-  return new PoolAsyncCompletion(c);
-}
-
-librados::AioCompletion *librados::Rados::aio_create_completion()
-{
-  AioCompletionImpl *c = new AioCompletionImpl;
-  return new AioCompletion(c);
-}
-
-librados::AioCompletion *librados::Rados::aio_create_completion(void *cb_arg,
-                                                               callback_t cb_complete,
-                                                               callback_t cb_safe)
-{
-  AioCompletionImpl *c;
-  int r = rados_aio_create_completion(cb_arg, cb_complete, cb_safe, (void**)&c);
-  ceph_assert(r == 0);
-  return new AioCompletion(c);
-}
-
-librados::ObjectOperation::ObjectOperation()
-{
-  impl = new ObjectOperationImpl;
-}
-
-librados::ObjectOperation::~ObjectOperation()
-{
-  delete impl;
-}
-
-///////////////////////////// C API //////////////////////////////
-
-static CephContext *rados_create_cct(const char * const clustername,
-                                     CephInitParameters *iparams)
-{
-  // missing things compared to global_init:
-  // g_ceph_context, g_conf, g_lockdep, signal handlers
-  CephContext *cct = common_preinit(*iparams, CODE_ENVIRONMENT_LIBRARY, 0);
-  if (clustername)
-    cct->_conf->cluster = clustername;
-  cct->_conf.parse_env(); // environment variables override
-  cct->_conf.apply_changes(nullptr);
-
-  TracepointProvider::initialize<tracepoint_traits>(cct);
-  return cct;
-}
-
-extern "C" int rados_create(rados_t *pcluster, const char * const id)
-{
-  CephInitParameters iparams(CEPH_ENTITY_TYPE_CLIENT);
-  if (id) {
-    iparams.name.set(CEPH_ENTITY_TYPE_CLIENT, id);
-  }
-  CephContext *cct = rados_create_cct("", &iparams);
-
-  tracepoint(librados, rados_create_enter, id);
-  *pcluster = reinterpret_cast<rados_t>(new librados::RadosClient(cct));
-  tracepoint(librados, rados_create_exit, 0, *pcluster);
-
-  cct->put();
-  return 0;
-}
-
-// as above, but
-// 1) don't assume 'client.'; name is a full type.id namestr
-// 2) allow setting clustername
-// 3) flags is for future expansion (maybe some of the global_init()
-//    behavior is appropriate for some consumers of librados, for instance)
-
-extern "C" int rados_create2(rados_t *pcluster, const char *const clustername,
-                            const char * const name, uint64_t flags)
-{
-  // client is assumed, but from_str will override
-  int retval = 0;
-  CephInitParameters iparams(CEPH_ENTITY_TYPE_CLIENT);
-  if (!name || !iparams.name.from_str(name)) {
-    retval = -EINVAL;
-  }
-
-  CephContext *cct = rados_create_cct(clustername, &iparams);
-  tracepoint(librados, rados_create2_enter, clustername, name, flags);
-  if (retval == 0) {
-    *pcluster = reinterpret_cast<rados_t>(new librados::RadosClient(cct));
-  }
-  tracepoint(librados, rados_create2_exit, retval, *pcluster);
-
-  cct->put();
-  return retval;
-}
-
-/* This function is intended for use by Ceph daemons. These daemons have
- * already called global_init and want to use that particular configuration for
- * their cluster.
- */
-extern "C" int rados_create_with_context(rados_t *pcluster, rados_config_t cct_)
-{
-  CephContext *cct = (CephContext *)cct_;
-  TracepointProvider::initialize<tracepoint_traits>(cct);
-
-  tracepoint(librados, rados_create_with_context_enter, cct_);
-  librados::RadosClient *radosp = new librados::RadosClient(cct);
-  *pcluster = (void *)radosp;
-  tracepoint(librados, rados_create_with_context_exit, 0, *pcluster);
-  return 0;
-}
-
-extern "C" rados_config_t rados_cct(rados_t cluster)
-{
-  tracepoint(librados, rados_cct_enter, cluster);
-  librados::RadosClient *client = (librados::RadosClient *)cluster;
-  rados_config_t retval = (rados_config_t)client->cct;
-  tracepoint(librados, rados_cct_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_connect(rados_t cluster)
-{
-  tracepoint(librados, rados_connect_enter, cluster);
-  librados::RadosClient *client = (librados::RadosClient *)cluster;
-  int retval = client->connect();
-  tracepoint(librados, rados_connect_exit, retval);
-  return retval;
-}
-
-extern "C" void rados_shutdown(rados_t cluster)
-{
-  tracepoint(librados, rados_shutdown_enter, cluster);
-  librados::RadosClient *radosp = (librados::RadosClient *)cluster;
-  radosp->shutdown();
-  delete radosp;
-  tracepoint(librados, rados_shutdown_exit);
-}
-
-extern "C" uint64_t rados_get_instance_id(rados_t cluster)
-{
-  tracepoint(librados, rados_get_instance_id_enter, cluster);
-  librados::RadosClient *client = (librados::RadosClient *)cluster;
-  uint64_t retval = client->get_instance_id();
-  tracepoint(librados, rados_get_instance_id_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_get_min_compatible_osd(rados_t cluster,
-                                            int8_t* require_osd_release)
-{
-  librados::RadosClient *client = (librados::RadosClient *)cluster;
-  return client->get_min_compatible_osd(require_osd_release);
-}
-
-extern "C" int rados_get_min_compatible_client(rados_t cluster,
-                                               int8_t* min_compat_client,
-                                               int8_t* require_min_compat_client)
-{
-  librados::RadosClient *client = (librados::RadosClient *)cluster;
-  return client->get_min_compatible_client(min_compat_client,
-                                           require_min_compat_client);
-}
-
-extern "C" void rados_version(int *major, int *minor, int *extra)
-{
-  tracepoint(librados, rados_version_enter, major, minor, extra);
-  if (major)
-    *major = LIBRADOS_VER_MAJOR;
-  if (minor)
-    *minor = LIBRADOS_VER_MINOR;
-  if (extra)
-    *extra = LIBRADOS_VER_EXTRA;
-  tracepoint(librados, rados_version_exit, LIBRADOS_VER_MAJOR, LIBRADOS_VER_MINOR, LIBRADOS_VER_EXTRA);
-}
-
-
-// -- config --
-extern "C" int rados_conf_read_file(rados_t cluster, const char *path_list)
-{
-  tracepoint(librados, rados_conf_read_file_enter, cluster, path_list);
-  librados::RadosClient *client = (librados::RadosClient *)cluster;
-  auto& conf = client->cct->_conf;
-  ostringstream warnings;
-  int ret = conf.parse_config_files(path_list, &warnings, 0);
-  if (ret) {
-    if (warnings.tellp() > 0)
-      lderr(client->cct) << warnings.str() << dendl;
-    client->cct->_conf.complain_about_parse_errors(client->cct);
-    tracepoint(librados, rados_conf_read_file_exit, ret);
-    return ret;
-  }
-  conf.parse_env(); // environment variables override
-
-  conf.apply_changes(nullptr);
-  client->cct->_conf.complain_about_parse_errors(client->cct);
-  tracepoint(librados, rados_conf_read_file_exit, 0);
-  return 0;
-}
-
-extern "C" int rados_conf_parse_argv(rados_t cluster, int argc, const char **argv)
-{
-  tracepoint(librados, rados_conf_parse_argv_enter, cluster, argc);
-  int i;
-  for(i = 0; i < argc; i++) {
-    tracepoint(librados, rados_conf_parse_argv_arg, argv[i]);
-  }
-  librados::RadosClient *client = (librados::RadosClient *)cluster;
-  auto& conf = client->cct->_conf;
-  vector<const char*> args;
-  argv_to_vec(argc, argv, args);
-  int ret = conf.parse_argv(args);
-  if (ret) {
-    tracepoint(librados, rados_conf_parse_argv_exit, ret);
-    return ret;
-  }
-  conf.apply_changes(nullptr);
-  tracepoint(librados, rados_conf_parse_argv_exit, 0);
-  return 0;
-}
-
-// like above, but return the remainder of argv to contain remaining
-// unparsed args.  Must be allocated to at least argc by caller.
-// remargv will contain n <= argc pointers to original argv[], the end
-// of which may be NULL
-
-extern "C" int rados_conf_parse_argv_remainder(rados_t cluster, int argc,
-                                              const char **argv,
-                                              const char **remargv)
-{
-  tracepoint(librados, rados_conf_parse_argv_remainder_enter, cluster, argc);
-  unsigned int i;
-  for(i = 0; i < (unsigned int) argc; i++) {
-    tracepoint(librados, rados_conf_parse_argv_remainder_arg, argv[i]);
-  }
-  librados::RadosClient *client = (librados::RadosClient *)cluster;
-  auto& conf = client->cct->_conf;
-  vector<const char*> args;
-  for (int i=0; i<argc; i++)
-    args.push_back(argv[i]);
-  int ret = conf.parse_argv(args);
-  if (ret) {
-    tracepoint(librados, rados_conf_parse_argv_remainder_exit, ret);
-    return ret;
-  }
-  conf.apply_changes(NULL);
-  ceph_assert(args.size() <= (unsigned int)argc);
-  for (i = 0; i < (unsigned int)argc; ++i) {
-    if (i < args.size())
-      remargv[i] = args[i];
-    else
-      remargv[i] = (const char *)NULL;
-    tracepoint(librados, rados_conf_parse_argv_remainder_remarg, remargv[i]);
-  }
-  tracepoint(librados, rados_conf_parse_argv_remainder_exit, 0);
-  return 0;
-}
-
-extern "C" int rados_conf_parse_env(rados_t cluster, const char *env)
-{
-  tracepoint(librados, rados_conf_parse_env_enter, cluster, env);
-  librados::RadosClient *client = (librados::RadosClient *)cluster;
-  auto& conf = client->cct->_conf;
-  conf.parse_env(env);
-  conf.apply_changes(nullptr);
-  tracepoint(librados, rados_conf_parse_env_exit, 0);
-  return 0;
-}
-
-extern "C" int rados_conf_set(rados_t cluster, const char *option, const char *value)
-{
-  tracepoint(librados, rados_conf_set_enter, cluster, option, value);
-  librados::RadosClient *client = (librados::RadosClient *)cluster;
-  auto& conf = client->cct->_conf;
-  int ret = conf.set_val(option, value);
-  if (ret) {
-    tracepoint(librados, rados_conf_set_exit, ret);
-    return ret;
-  }
-  conf.apply_changes(nullptr);
-  tracepoint(librados, rados_conf_set_exit, 0);
-  return 0;
-}
-
-/* cluster info */
-extern "C" int rados_cluster_stat(rados_t cluster, rados_cluster_stat_t *result)
-{
-  tracepoint(librados, rados_cluster_stat_enter, cluster);
-  librados::RadosClient *client = (librados::RadosClient *)cluster;
-
-  ceph_statfs stats;
-  int r = client->get_fs_stats(stats);
-  result->kb = stats.kb;
-  result->kb_used = stats.kb_used;
-  result->kb_avail = stats.kb_avail;
-  result->num_objects = stats.num_objects;
-  tracepoint(librados, rados_cluster_stat_exit, r, result->kb, result->kb_used, result->kb_avail, result->num_objects);
-  return r;
-}
-
-extern "C" int rados_conf_get(rados_t cluster, const char *option, char *buf, size_t len)
-{
-  tracepoint(librados, rados_conf_get_enter, cluster, option, len);
-  char *tmp = buf;
-  librados::RadosClient *client = (librados::RadosClient *)cluster;
-  const auto& conf = client->cct->_conf;
-  int retval = conf.get_val(option, &tmp, len);
-  tracepoint(librados, rados_conf_get_exit, retval, retval ? "" : option);
-  return retval;
-}
-
-extern "C" int64_t rados_pool_lookup(rados_t cluster, const char *name)
-{
-  tracepoint(librados, rados_pool_lookup_enter, cluster, name);
-  librados::RadosClient *radosp = (librados::RadosClient *)cluster;
-  int64_t retval = radosp->lookup_pool(name);
-  tracepoint(librados, rados_pool_lookup_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_pool_reverse_lookup(rados_t cluster, int64_t id,
-                                        char *buf, size_t maxlen)
-{
-  tracepoint(librados, rados_pool_reverse_lookup_enter, cluster, id, maxlen);
-  librados::RadosClient *radosp = (librados::RadosClient *)cluster;
-  std::string name;
-  int r = radosp->pool_get_name(id, &name);
-  if (r < 0) {
-    tracepoint(librados, rados_pool_reverse_lookup_exit, r, "");
-    return r;
-  }
-  if (name.length() >= maxlen) {
-    tracepoint(librados, rados_pool_reverse_lookup_exit, -ERANGE, "");
-    return -ERANGE;
-  }
-  strcpy(buf, name.c_str());
-  int retval = name.length();
-  tracepoint(librados, rados_pool_reverse_lookup_exit, retval, buf);
-  return retval;
-}
-
-extern "C" int rados_cluster_fsid(rados_t cluster, char *buf,
-                                 size_t maxlen)
-{
-  tracepoint(librados, rados_cluster_fsid_enter, cluster, maxlen);
-  librados::RadosClient *radosp = (librados::RadosClient *)cluster;
-  std::string fsid;
-  radosp->get_fsid(&fsid);
-  if (fsid.length() >= maxlen) {
-    tracepoint(librados, rados_cluster_fsid_exit, -ERANGE, "");
-    return -ERANGE;
-  }
-  strcpy(buf, fsid.c_str());
-  int retval = fsid.length();
-  tracepoint(librados, rados_cluster_fsid_exit, retval, buf);
-  return retval;
-}
-
-extern "C" int rados_wait_for_latest_osdmap(rados_t cluster)
-{
-  tracepoint(librados, rados_wait_for_latest_osdmap_enter, cluster);
-  librados::RadosClient *radosp = (librados::RadosClient *)cluster;
-  int retval = radosp->wait_for_latest_osdmap();
-  tracepoint(librados, rados_wait_for_latest_osdmap_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_blacklist_add(rados_t cluster, char *client_address,
-                                  uint32_t expire_seconds)
-{
-  librados::RadosClient *radosp = (librados::RadosClient *)cluster;
-  return radosp->blacklist_add(client_address, expire_seconds);
-}
-
-extern "C" void rados_set_osdmap_full_try(rados_ioctx_t io)
-{
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  ctx->objecter->set_osdmap_full_try();
-}
-
-extern "C" void rados_unset_osdmap_full_try(rados_ioctx_t io)
-{
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  ctx->objecter->unset_osdmap_full_try();
-}
-
-extern "C" int rados_application_enable(rados_ioctx_t io, const char *app_name,
-                                        int force)
-{
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  return ctx->application_enable(app_name, force != 0);
-}
-
-extern "C" int rados_application_list(rados_ioctx_t io, char *values,
-                                      size_t *values_len)
-{
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  std::set<std::string> app_names;
-  int r = ctx->application_list(&app_names);
-  if (r < 0) {
-    return r;
-  }
-
-  size_t total_len = 0;
-  for (auto app_name : app_names) {
-    total_len += app_name.size() + 1;
-  }
-
-  if (*values_len < total_len) {
-    *values_len = total_len;
-    return -ERANGE;
-  }
-
-  char *values_p = values;
-  for (auto app_name : app_names) {
-    size_t len = app_name.size() + 1;
-    strncpy(values_p, app_name.c_str(), len);
-    values_p += len;
-  }
-  *values_p = '\0';
-  *values_len = total_len;
-  return 0;
-}
-
-extern "C" int rados_application_metadata_get(rados_ioctx_t io,
-                                              const char *app_name,
-                                              const char *key, char *value,
-                                              size_t *value_len)
-{
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  std::string value_str;
-  int r = ctx->application_metadata_get(app_name, key, &value_str);
-  if (r < 0) {
-    return r;
-  }
-
-  size_t len = value_str.size() + 1;
-  if (*value_len < len) {
-    *value_len = len;
-    return -ERANGE;
-  }
-
-  strncpy(value, value_str.c_str(), len);
-  *value_len = len;
-  return 0;
-}
-
-extern "C" int rados_application_metadata_set(rados_ioctx_t io,
-                                              const char *app_name,
-                                              const char *key,
-                                              const char *value)
-{
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  return ctx->application_metadata_set(app_name, key, value);
-}
-
-extern "C" int rados_application_metadata_remove(rados_ioctx_t io,
-                                                 const char *app_name,
-                                                 const char *key)
-{
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  return ctx->application_metadata_remove(app_name, key);
-}
-
-extern "C" int rados_application_metadata_list(rados_ioctx_t io,
-                                               const char *app_name,
-                                               char *keys, size_t *keys_len,
-                                               char *values, size_t *vals_len)
-{
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  std::map<std::string, std::string> metadata;
-  int r = ctx->application_metadata_list(app_name, &metadata);
-  if (r < 0) {
-    return r;
-  }
-
-  size_t total_key_len = 0;
-  size_t total_val_len = 0;
-  for (auto pair : metadata) {
-    total_key_len += pair.first.size() + 1;
-    total_val_len += pair.second.size() + 1;
-  }
-
-  if (*keys_len < total_key_len || *vals_len < total_val_len) {
-    *keys_len = total_key_len;
-    *vals_len = total_val_len;
-    return -ERANGE;
-  }
-
-  char *keys_p = keys;
-  char *vals_p = values;
-  for (auto pair : metadata) {
-    size_t key_len = pair.first.size() + 1;
-    strncpy(keys_p, pair.first.c_str(), key_len);
-    keys_p += key_len;
-
-    size_t val_len = pair.second.size() + 1;
-    strncpy(vals_p, pair.second.c_str(), val_len);
-    vals_p += val_len;
-  }
-  *keys_p = '\0';
-  *keys_len = total_key_len;
-
-  *vals_p = '\0';
-  *vals_len = total_val_len;
-  return 0;
-}
-
-extern "C" int rados_pool_list(rados_t cluster, char *buf, size_t len)
-{
-  tracepoint(librados, rados_pool_list_enter, cluster, len);
-  librados::RadosClient *client = (librados::RadosClient *)cluster;
-  std::list<std::pair<int64_t, std::string> > pools;
-  int r = client->pool_list(pools);
-  if (r < 0) {
-    tracepoint(librados, rados_pool_list_exit, r);
-    return r;
-  }
-
-  if (len > 0 && !buf) {
-    tracepoint(librados, rados_pool_list_exit, -EINVAL);
-    return -EINVAL;
-  }
-
-  char *b = buf;
-  if (b)
-    memset(b, 0, len);
-  int needed = 0;
-  std::list<std::pair<int64_t, std::string> >::const_iterator i = pools.begin();
-  std::list<std::pair<int64_t, std::string> >::const_iterator p_end =
-    pools.end();
-  for (; i != p_end; ++i) {
-    int rl = i->second.length() + 1;
-    if (len < (unsigned)rl)
-      break;
-    const char* pool = i->second.c_str();
-    tracepoint(librados, rados_pool_list_pool, pool);
-    if (b) {
-      strncat(b, pool, rl);
-      b += rl;
-    }
-    needed += rl;
-    len -= rl;
-  }
-  for (; i != p_end; ++i) {
-    int rl = i->second.length() + 1;
-    needed += rl;
-  }
-  int retval = needed + 1;
-  tracepoint(librados, rados_pool_list_exit, retval);
-  return retval;
-}
-
-CEPH_RADOS_API int rados_inconsistent_pg_list(rados_t cluster, int64_t pool_id,
-                                             char *buf, size_t len)
-{
-  tracepoint(librados, rados_inconsistent_pg_list_enter, cluster, pool_id, len);
-  librados::RadosClient *client = (librados::RadosClient *)cluster;
-  std::vector<std::string> pgs;
-  if (int r = client->get_inconsistent_pgs(pool_id, &pgs); r < 0) {
-    tracepoint(librados, rados_inconsistent_pg_list_exit, r);
-    return r;
-  }
-
-  if (len > 0 && !buf) {
-    tracepoint(librados, rados_inconsistent_pg_list_exit, -EINVAL);
-    return -EINVAL;
-  }
-
-  char *b = buf;
-  if (b)
-    memset(b, 0, len);
-  int needed = 0;
-  for (const auto& s : pgs) {
-    unsigned rl = s.length() + 1;
-    if (b && len >= rl) {
-      tracepoint(librados, rados_inconsistent_pg_list_pg, s.c_str());
-      strncat(b, s.c_str(), rl);
-      b += rl;
-      len -= rl;
-    }
-    needed += rl;
-  }
-  int retval = needed + 1;
-  tracepoint(librados, rados_inconsistent_pg_list_exit, retval);
-  return retval;
-}
-
-
-static void dict_to_map(const char *dict,
-                        std::map<std::string, std::string>* dict_map)
-{
-  while (*dict != '\0') {
-    const char* key = dict;
-    dict += strlen(key) + 1;
-    const char* value = dict;
-    dict += strlen(value) + 1;
-    (*dict_map)[key] = value;
-  }
-}
-
-CEPH_RADOS_API int rados_service_register(rados_t cluster, const char *service,
-                                          const char *daemon,
-                                          const char *metadata_dict)
-{
-  librados::RadosClient *client = (librados::RadosClient *)cluster;
-
-  std::map<std::string, std::string> metadata;
-  dict_to_map(metadata_dict, &metadata);
-
-  return client->service_daemon_register(service, daemon, metadata);
-}
-
-CEPH_RADOS_API int rados_service_update_status(rados_t cluster,
-                                               const char *status_dict)
-{
-  librados::RadosClient *client = (librados::RadosClient *)cluster;
-
-  std::map<std::string, std::string> status;
-  dict_to_map(status_dict, &status);
-
-  return client->service_daemon_update_status(std::move(status));
-}
-
-static void do_out_buffer(bufferlist& outbl, char **outbuf, size_t *outbuflen)
-{
-  if (outbuf) {
-    if (outbl.length() > 0) {
-      *outbuf = (char *)malloc(outbl.length());
-      memcpy(*outbuf, outbl.c_str(), outbl.length());
-    } else {
-      *outbuf = NULL;
-    }
-  }
-  if (outbuflen)
-    *outbuflen = outbl.length();
-}
-
-static void do_out_buffer(string& outbl, char **outbuf, size_t *outbuflen)
-{
-  if (outbuf) {
-    if (outbl.length() > 0) {
-      *outbuf = (char *)malloc(outbl.length());
-      memcpy(*outbuf, outbl.c_str(), outbl.length());
-    } else {
-      *outbuf = NULL;
-    }
-  }
-  if (outbuflen)
-    *outbuflen = outbl.length();
-}
-
-extern "C" int rados_ping_monitor(rados_t cluster, const char *mon_id,
-                                  char **outstr, size_t *outstrlen)
-{
-  tracepoint(librados, rados_ping_monitor_enter, cluster, mon_id);
-  librados::RadosClient *client = (librados::RadosClient *)cluster;
-  string str;
-
-  if (!mon_id) {
-    tracepoint(librados, rados_ping_monitor_exit, -EINVAL, NULL, NULL);
-    return -EINVAL;
-  }
-
-  int ret = client->ping_monitor(mon_id, &str);
-  if (ret == 0) {
-    do_out_buffer(str, outstr, outstrlen);
-  }
-  tracepoint(librados, rados_ping_monitor_exit, ret, ret < 0 ? NULL : outstr, ret < 0 ? NULL : outstrlen);
-  return ret;
-}
-
-extern "C" int rados_mon_command(rados_t cluster, const char **cmd,
-                                size_t cmdlen,
-                                const char *inbuf, size_t inbuflen,
-                                char **outbuf, size_t *outbuflen,
-                                char **outs, size_t *outslen)
-{
-  tracepoint(librados, rados_mon_command_enter, cluster, cmdlen, inbuf, inbuflen);
-  librados::RadosClient *client = (librados::RadosClient *)cluster;
-  bufferlist inbl;
-  bufferlist outbl;
-  string outstring;
-  vector<string> cmdvec;
-
-  for (size_t i = 0; i < cmdlen; i++) {
-    tracepoint(librados, rados_mon_command_cmd, cmd[i]);
-    cmdvec.push_back(cmd[i]);
-  }
-
-  inbl.append(inbuf, inbuflen);
-  int ret = client->mon_command(cmdvec, inbl, &outbl, &outstring);
-
-  do_out_buffer(outbl, outbuf, outbuflen);
-  do_out_buffer(outstring, outs, outslen);
-  tracepoint(librados, rados_mon_command_exit, ret, outbuf, outbuflen, outs, outslen);
-  return ret;
-}
-
-extern "C" int rados_mon_command_target(rados_t cluster, const char *name,
-                                       const char **cmd,
-                                       size_t cmdlen,
-                                       const char *inbuf, size_t inbuflen,
-                                       char **outbuf, size_t *outbuflen,
-                                       char **outs, size_t *outslen)
-{
-  tracepoint(librados, rados_mon_command_target_enter, cluster, name, cmdlen, inbuf, inbuflen);
-  librados::RadosClient *client = (librados::RadosClient *)cluster;
-  bufferlist inbl;
-  bufferlist outbl;
-  string outstring;
-  vector<string> cmdvec;
-
-  // is this a numeric id?
-  char *endptr;
-  errno = 0;
-  long rank = strtol(name, &endptr, 10);
-  if ((errno == ERANGE && (rank == LONG_MAX || rank == LONG_MIN)) ||
-      (errno != 0 && rank == 0) ||
-      endptr == name ||    // no digits
-      *endptr != '\0') {   // extra characters
-    rank = -1;
-  }
-
-  for (size_t i = 0; i < cmdlen; i++) {
-    tracepoint(librados, rados_mon_command_target_cmd, cmd[i]);
-    cmdvec.push_back(cmd[i]);
-  }
-
-  inbl.append(inbuf, inbuflen);
-  int ret;
-  if (rank >= 0)
-    ret = client->mon_command(rank, cmdvec, inbl, &outbl, &outstring);
-  else
-    ret = client->mon_command(name, cmdvec, inbl, &outbl, &outstring);
-
-  do_out_buffer(outbl, outbuf, outbuflen);
-  do_out_buffer(outstring, outs, outslen);
-  tracepoint(librados, rados_mon_command_target_exit, ret, outbuf, outbuflen, outs, outslen);
-  return ret;
-}
-
-extern "C" int rados_osd_command(rados_t cluster, int osdid, const char **cmd,
-                                size_t cmdlen,
-                                const char *inbuf, size_t inbuflen,
-                                char **outbuf, size_t *outbuflen,
-                                char **outs, size_t *outslen)
-{
-  tracepoint(librados, rados_osd_command_enter, cluster, osdid, cmdlen, inbuf, inbuflen);
-  librados::RadosClient *client = (librados::RadosClient *)cluster;
-  bufferlist inbl;
-  bufferlist outbl;
-  string outstring;
-  vector<string> cmdvec;
-
-  for (size_t i = 0; i < cmdlen; i++) {
-    tracepoint(librados, rados_osd_command_cmd, cmd[i]);
-    cmdvec.push_back(cmd[i]);
-  }
-
-  inbl.append(inbuf, inbuflen);
-  int ret = client->osd_command(osdid, cmdvec, inbl, &outbl, &outstring);
-
-  do_out_buffer(outbl, outbuf, outbuflen);
-  do_out_buffer(outstring, outs, outslen);
-  tracepoint(librados, rados_osd_command_exit, ret, outbuf, outbuflen, outs, outslen);
-  return ret;
-}
-
-extern "C" int rados_mgr_command(rados_t cluster, const char **cmd,
-                                size_t cmdlen,
-                                const char *inbuf, size_t inbuflen,
-                                char **outbuf, size_t *outbuflen,
-                                char **outs, size_t *outslen)
-{
-  tracepoint(librados, rados_mgr_command_enter, cluster, cmdlen, inbuf,
-      inbuflen);
-
-  librados::RadosClient *client = (librados::RadosClient *)cluster;
-  bufferlist inbl;
-  bufferlist outbl;
-  string outstring;
-  vector<string> cmdvec;
-
-  for (size_t i = 0; i < cmdlen; i++) {
-    tracepoint(librados, rados_mgr_command_cmd, cmd[i]);
-    cmdvec.push_back(cmd[i]);
-  }
-
-  inbl.append(inbuf, inbuflen);
-  int ret = client->mgr_command(cmdvec, inbl, &outbl, &outstring);
-
-  do_out_buffer(outbl, outbuf, outbuflen);
-  do_out_buffer(outstring, outs, outslen);
-  tracepoint(librados, rados_mgr_command_exit, ret, outbuf, outbuflen, outs,
-      outslen);
-  return ret;
-}
-
-extern "C" int rados_pg_command(rados_t cluster, const char *pgstr,
-                               const char **cmd, size_t cmdlen,
-                               const char *inbuf, size_t inbuflen,
-                               char **outbuf, size_t *outbuflen,
-                               char **outs, size_t *outslen)
-{
-  tracepoint(librados, rados_pg_command_enter, cluster, pgstr, cmdlen, inbuf, inbuflen);
-  librados::RadosClient *client = (librados::RadosClient *)cluster;
-  bufferlist inbl;
-  bufferlist outbl;
-  string outstring;
-  pg_t pgid;
-  vector<string> cmdvec;
-
-  for (size_t i = 0; i < cmdlen; i++) {
-    tracepoint(librados, rados_pg_command_cmd, cmd[i]);
-    cmdvec.push_back(cmd[i]);
-  }
-
-  inbl.append(inbuf, inbuflen);
-  if (!pgid.parse(pgstr))
-    return -EINVAL;
-
-  int ret = client->pg_command(pgid, cmdvec, inbl, &outbl, &outstring);
-
-  do_out_buffer(outbl, outbuf, outbuflen);
-  do_out_buffer(outstring, outs, outslen);
-  tracepoint(librados, rados_pg_command_exit, ret, outbuf, outbuflen, outs, outslen);
-  return ret;
-}
-
-extern "C" void rados_buffer_free(char *buf)
-{
-  tracepoint(librados, rados_buffer_free_enter, buf);
-  if (buf)
-    free(buf);
-  tracepoint(librados, rados_buffer_free_exit);
-}
-
-extern "C" int rados_monitor_log(rados_t cluster, const char *level, rados_log_callback_t cb, void *arg)
-{
-  tracepoint(librados, rados_monitor_log_enter, cluster, level, cb, arg);
-  librados::RadosClient *client = (librados::RadosClient *)cluster;
-  int retval = client->monitor_log(level, cb, nullptr, arg);
-  tracepoint(librados, rados_monitor_log_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_monitor_log2(rados_t cluster, const char *level,
-                                 rados_log_callback2_t cb, void *arg)
-{
-  tracepoint(librados, rados_monitor_log2_enter, cluster, level, cb, arg);
-  librados::RadosClient *client = (librados::RadosClient *)cluster;
-  int retval = client->monitor_log(level, nullptr, cb, arg);
-  tracepoint(librados, rados_monitor_log2_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_ioctx_create(rados_t cluster, const char *name, rados_ioctx_t *io)
-{
-  tracepoint(librados, rados_ioctx_create_enter, cluster, name);
-  librados::RadosClient *client = (librados::RadosClient *)cluster;
-  librados::IoCtxImpl *ctx;
-
-  int r = client->create_ioctx(name, &ctx);
-  if (r < 0) {
-    tracepoint(librados, rados_ioctx_create_exit, r, NULL);
-    return r;
-  }
-
-  *io = ctx;
-  ctx->get();
-  tracepoint(librados, rados_ioctx_create_exit, 0, ctx);
-  return 0;
-}
-
-extern "C" int rados_ioctx_create2(rados_t cluster, int64_t pool_id,
-                                   rados_ioctx_t *io)
-{
-  tracepoint(librados, rados_ioctx_create2_enter, cluster, pool_id);
-  librados::RadosClient *client = (librados::RadosClient *)cluster;
-  librados::IoCtxImpl *ctx;
-
-  int r = client->create_ioctx(pool_id, &ctx);
-  if (r < 0) {
-    tracepoint(librados, rados_ioctx_create2_exit, r, NULL);
-    return r;
-  }
-
-  *io = ctx;
-  ctx->get();
-  tracepoint(librados, rados_ioctx_create2_exit, 0, ctx);
-  return 0;
-}
-
-extern "C" void rados_ioctx_destroy(rados_ioctx_t io)
-{
-  tracepoint(librados, rados_ioctx_destroy_enter, io);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  ctx->put();
-  tracepoint(librados, rados_ioctx_destroy_exit);
-}
-
-extern "C" int rados_ioctx_pool_stat(rados_ioctx_t io, struct rados_pool_stat_t *stats)
-{
-  tracepoint(librados, rados_ioctx_pool_stat_enter, io);
-  librados::IoCtxImpl *io_ctx_impl = (librados::IoCtxImpl *)io;
-  list<string> ls;
-  std::string pool_name;
-
-  int err = io_ctx_impl->client->pool_get_name(io_ctx_impl->get_id(), &pool_name);
-  if (err) {
-    tracepoint(librados, rados_ioctx_pool_stat_exit, err, stats);
-    return err;
-  }
-  ls.push_back(pool_name);
-
-  map<string, ::pool_stat_t> rawresult;
-  err = io_ctx_impl->client->get_pool_stats(ls, rawresult);
-  if (err) {
-    tracepoint(librados, rados_ioctx_pool_stat_exit, err, stats);
-    return err;
-  }
-
-  ::pool_stat_t& r = rawresult[pool_name];
-  stats->num_kb = shift_round_up(r.stats.sum.num_bytes, 10);
-  stats->num_bytes = r.stats.sum.num_bytes;
-  stats->num_objects = r.stats.sum.num_objects;
-  stats->num_object_clones = r.stats.sum.num_object_clones;
-  stats->num_object_copies = r.stats.sum.num_object_copies;
-  stats->num_objects_missing_on_primary = r.stats.sum.num_objects_missing_on_primary;
-  stats->num_objects_unfound = r.stats.sum.num_objects_unfound;
-  stats->num_objects_degraded =
-    r.stats.sum.num_objects_degraded +
-    r.stats.sum.num_objects_misplaced; // FIXME: this is imprecise
-  stats->num_rd = r.stats.sum.num_rd;
-  stats->num_rd_kb = r.stats.sum.num_rd_kb;
-  stats->num_wr = r.stats.sum.num_wr;
-  stats->num_wr_kb = r.stats.sum.num_wr_kb;
-  tracepoint(librados, rados_ioctx_pool_stat_exit, 0, stats);
-  return 0;
-}
-
-extern "C" rados_config_t rados_ioctx_cct(rados_ioctx_t io)
-{
-  tracepoint(librados, rados_ioctx_cct_enter, io);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  rados_config_t retval = (rados_config_t)ctx->client->cct;
-  tracepoint(librados, rados_ioctx_cct_exit, retval);
-  return retval;
-}
-
-extern "C" void rados_ioctx_snap_set_read(rados_ioctx_t io, rados_snap_t seq)
-{
-  tracepoint(librados, rados_ioctx_snap_set_read_enter, io, seq);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  ctx->set_snap_read((snapid_t)seq);
-  tracepoint(librados, rados_ioctx_snap_set_read_exit);
-}
-
-extern "C" int rados_ioctx_selfmanaged_snap_set_write_ctx(rados_ioctx_t io,
-           rados_snap_t seq, rados_snap_t *snaps, int num_snaps)
-{
-  tracepoint(librados, rados_ioctx_selfmanaged_snap_set_write_ctx_enter, io, seq, snaps, num_snaps);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  vector<snapid_t> snv;
-  snv.resize(num_snaps);
-  for (int i=0; i<num_snaps; i++) {
-    snv[i] = (snapid_t)snaps[i];
-  }
-  int retval = ctx->set_snap_write_context((snapid_t)seq, snv);
-  tracepoint(librados, rados_ioctx_selfmanaged_snap_set_write_ctx_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_write(rados_ioctx_t io, const char *o, const char *buf, size_t len, uint64_t off)
-{
-  tracepoint(librados, rados_write_enter, io, o, buf, len, off);
-  if (len > UINT_MAX/2)
-    return -E2BIG;
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  object_t oid(o);
-  bufferlist bl;
-  bl.append(buf, len);
-  int retval = ctx->write(oid, bl, len, off);
-  tracepoint(librados, rados_write_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_append(rados_ioctx_t io, const char *o, const char *buf, size_t len)
-{
-  tracepoint(librados, rados_append_enter, io, o, buf, len);
-  if (len > UINT_MAX/2)
-    return -E2BIG;
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  object_t oid(o);
-  bufferlist bl;
-  bl.append(buf, len);
-  int retval = ctx->append(oid, bl, len);
-  tracepoint(librados, rados_append_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_write_full(rados_ioctx_t io, const char *o, const char *buf, size_t len)
-{
-  tracepoint(librados, rados_write_full_enter, io, o, buf, len);
-  if (len > UINT_MAX/2)
-    return -E2BIG;
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  object_t oid(o);
-  bufferlist bl;
-  bl.append(buf, len);
-  int retval = ctx->write_full(oid, bl);
-  tracepoint(librados, rados_write_full_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_writesame(rados_ioctx_t io,
-                               const char *o,
-                               const char *buf,
-                               size_t data_len,
-                               size_t write_len,
-                               uint64_t off)
-{
-  tracepoint(librados, rados_writesame_enter, io, o, buf, data_len, write_len, off);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  object_t oid(o);
-  bufferlist bl;
-  bl.append(buf, data_len);
-  int retval = ctx->writesame(oid, bl, write_len, off);
-  tracepoint(librados, rados_writesame_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_trunc(rados_ioctx_t io, const char *o, uint64_t size)
-{
-  tracepoint(librados, rados_trunc_enter, io, o, size);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  object_t oid(o);
-  int retval = ctx->trunc(oid, size);
-  tracepoint(librados, rados_trunc_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_remove(rados_ioctx_t io, const char *o)
-{
-  tracepoint(librados, rados_remove_enter, io, o);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  object_t oid(o);
-  int retval = ctx->remove(oid);
-  tracepoint(librados, rados_remove_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_read(rados_ioctx_t io, const char *o, char *buf, size_t len, uint64_t off)
-{
-  tracepoint(librados, rados_read_enter, io, o, buf, len, off);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  int ret;
-  object_t oid(o);
-
-  bufferlist bl;
-  bufferptr bp = buffer::create_static(len, buf);
-  bl.push_back(bp);
-
-  ret = ctx->read(oid, bl, len, off);
-  if (ret >= 0) {
-    if (bl.length() > len) {
-      tracepoint(librados, rados_read_exit, -ERANGE, NULL);
-      return -ERANGE;
-    }
-    if (!bl.is_provided_buffer(buf))
-      bl.copy(0, bl.length(), buf);
-    ret = bl.length();    // hrm :/
-  }
-
-  tracepoint(librados, rados_read_exit, ret, buf);
-  return ret;
-}
-
-extern "C" int rados_checksum(rados_ioctx_t io, const char *o,
-                              rados_checksum_type_t type,
-                              const char *init_value, size_t init_value_len,
-                              size_t len, uint64_t off, size_t chunk_size,
-                             char *pchecksum, size_t checksum_len)
-{
-  tracepoint(librados, rados_checksum_enter, io, o, type, init_value,
-            init_value_len, len, off, chunk_size);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  object_t oid(o);
-
-  bufferlist init_value_bl;
-  init_value_bl.append(init_value, init_value_len);
-
-  bufferlist checksum_bl;
-
-  int retval = ctx->checksum(oid, get_checksum_op_type(type), init_value_bl,
-                            len, off, chunk_size, &checksum_bl);
-  if (retval >= 0) {
-    if (checksum_bl.length() > checksum_len) {
-      tracepoint(librados, rados_checksum_exit, -ERANGE, NULL, 0);
-      return -ERANGE;
-    }
-
-    checksum_bl.copy(0, checksum_bl.length(), pchecksum);
-  }
-  tracepoint(librados, rados_checksum_exit, retval, pchecksum, checksum_len);
-  return retval;
-}
-
-extern "C" uint64_t rados_get_last_version(rados_ioctx_t io)
-{
-  tracepoint(librados, rados_get_last_version_enter, io);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  uint64_t retval = ctx->last_version();
-  tracepoint(librados, rados_get_last_version_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_pool_create(rados_t cluster, const char *name)
-{
-  tracepoint(librados, rados_pool_create_enter, cluster, name);
-  librados::RadosClient *radosp = (librados::RadosClient *)cluster;
-  string sname(name);
-  int retval = radosp->pool_create(sname);
-  tracepoint(librados, rados_pool_create_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_pool_create_with_auid(rados_t cluster, const char *name,
-                                          uint64_t auid)
-{
-  tracepoint(librados, rados_pool_create_with_auid_enter, cluster, name, auid);
-  librados::RadosClient *radosp = (librados::RadosClient *)cluster;
-  string sname(name);
-  int retval = 0;
-  if (auid != CEPH_AUTH_UID_DEFAULT) {
-    retval = -EINVAL;
-  } else {
-    retval = radosp->pool_create(sname);
-  }
-  tracepoint(librados, rados_pool_create_with_auid_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_pool_create_with_crush_rule(rados_t cluster, const char *name,
-                                                __u8 crush_rule_num)
-{
-  tracepoint(librados, rados_pool_create_with_crush_rule_enter, cluster, name, crush_rule_num);
-  librados::RadosClient *radosp = (librados::RadosClient *)cluster;
-  string sname(name);
-  int retval = radosp->pool_create(sname, crush_rule_num);
-  tracepoint(librados, rados_pool_create_with_crush_rule_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_pool_create_with_all(rados_t cluster, const char *name,
-                                         uint64_t auid, __u8 crush_rule_num)
-{
-  tracepoint(librados, rados_pool_create_with_all_enter, cluster, name, auid, crush_rule_num);
-  librados::RadosClient *radosp = (librados::RadosClient *)cluster;
-  string sname(name);
-  int retval = 0;
-  if (auid != CEPH_AUTH_UID_DEFAULT) {
-    retval = -EINVAL;
-  } else {
-    retval = radosp->pool_create(sname, crush_rule_num);
-  }
-  tracepoint(librados, rados_pool_create_with_all_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_pool_get_base_tier(rados_t cluster, int64_t pool_id, int64_t* base_tier)
-{
-  tracepoint(librados, rados_pool_get_base_tier_enter, cluster, pool_id);
-  librados::RadosClient *client = (librados::RadosClient *)cluster;
-  int retval = client->pool_get_base_tier(pool_id, base_tier);
-  tracepoint(librados, rados_pool_get_base_tier_exit, retval, *base_tier);
-  return retval;
-}
-
-extern "C" int rados_pool_delete(rados_t cluster, const char *pool_name)
-{
-  tracepoint(librados, rados_pool_delete_enter, cluster, pool_name);
-  librados::RadosClient *client = (librados::RadosClient *)cluster;
-  int retval = client->pool_delete(pool_name);
-  tracepoint(librados, rados_pool_delete_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_ioctx_pool_set_auid(rados_ioctx_t io, uint64_t auid)
-{
-  tracepoint(librados, rados_ioctx_pool_set_auid_enter, io, auid);
-  int retval = -EOPNOTSUPP;
-  tracepoint(librados, rados_ioctx_pool_set_auid_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_ioctx_pool_get_auid(rados_ioctx_t io, uint64_t *auid)
-{
-  tracepoint(librados, rados_ioctx_pool_get_auid_enter, io);
-  int retval = -EOPNOTSUPP;
-  tracepoint(librados, rados_ioctx_pool_get_auid_exit, retval, *auid);
-  return retval;
-}
-
-extern "C" int rados_ioctx_pool_requires_alignment(rados_ioctx_t io)
-{
-  tracepoint(librados, rados_ioctx_pool_requires_alignment_enter, io);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  int retval = ctx->client->pool_requires_alignment(ctx->get_id());
-  tracepoint(librados, rados_ioctx_pool_requires_alignment_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_ioctx_pool_requires_alignment2(rados_ioctx_t io,
-       int *requires)
-{
-  tracepoint(librados, rados_ioctx_pool_requires_alignment_enter2, io);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  bool requires_alignment;
-  int retval = ctx->client->pool_requires_alignment2(ctx->get_id(), 
-       &requires_alignment);
-  tracepoint(librados, rados_ioctx_pool_requires_alignment_exit2, retval, 
-       requires_alignment);
-  if (requires)
-    *requires = requires_alignment;
-  return retval;
-}
-
-extern "C" uint64_t rados_ioctx_pool_required_alignment(rados_ioctx_t io)
-{
-  tracepoint(librados, rados_ioctx_pool_required_alignment_enter, io);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  uint64_t retval = ctx->client->pool_required_alignment(ctx->get_id());
-  tracepoint(librados, rados_ioctx_pool_required_alignment_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_ioctx_pool_required_alignment2(rados_ioctx_t io,
-       uint64_t *alignment)
-{
-  tracepoint(librados, rados_ioctx_pool_required_alignment_enter2, io);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  int retval = ctx->client->pool_required_alignment2(ctx->get_id(),
-       alignment);
-  tracepoint(librados, rados_ioctx_pool_required_alignment_exit2, retval, 
-       *alignment);
-  return retval;
-}
-
-extern "C" void rados_ioctx_locator_set_key(rados_ioctx_t io, const char *key)
-{
-  tracepoint(librados, rados_ioctx_locator_set_key_enter, io, key);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  if (key)
-    ctx->oloc.key = key;
-  else
-    ctx->oloc.key = "";
-  tracepoint(librados, rados_ioctx_locator_set_key_exit);
-}
-
-extern "C" void rados_ioctx_set_namespace(rados_ioctx_t io, const char *nspace)
-{
-  tracepoint(librados, rados_ioctx_set_namespace_enter, io, nspace);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  if (nspace)
-    ctx->oloc.nspace = nspace;
-  else
-    ctx->oloc.nspace = "";
-  tracepoint(librados, rados_ioctx_set_namespace_exit);
-}
-
-extern "C" int rados_ioctx_get_namespace(rados_ioctx_t io, char *s,
-                                         unsigned maxlen)
-{
-  tracepoint(librados, rados_ioctx_get_namespace_enter, io, maxlen);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  auto length = ctx->oloc.nspace.length();
-  if (length >= maxlen) {
-    tracepoint(librados, rados_ioctx_get_namespace_exit, -ERANGE, "");
-    return -ERANGE;
-  }
-  strcpy(s, ctx->oloc.nspace.c_str());
-  int retval = (int)length;
-  tracepoint(librados, rados_ioctx_get_namespace_exit, retval, s);
-  return retval;
-}
-
-extern "C" rados_t rados_ioctx_get_cluster(rados_ioctx_t io)
-{
-  tracepoint(librados, rados_ioctx_get_cluster_enter, io);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  rados_t retval = (rados_t)ctx->client;
-  tracepoint(librados, rados_ioctx_get_cluster_exit, retval);
-  return retval;
-}
-
-extern "C" int64_t rados_ioctx_get_id(rados_ioctx_t io)
-{
-  tracepoint(librados, rados_ioctx_get_id_enter, io);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  int64_t retval = ctx->get_id();
-  tracepoint(librados, rados_ioctx_get_id_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_ioctx_get_pool_name(rados_ioctx_t io, char *s, unsigned maxlen)
-{
-  tracepoint(librados, rados_ioctx_get_pool_name_enter, io, maxlen);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  std::string pool_name;
-
-  int err = ctx->client->pool_get_name(ctx->get_id(), &pool_name);
-  if (err) {
-    tracepoint(librados, rados_ioctx_get_pool_name_exit, err, "");
-    return err;
-  }
-  if (pool_name.length() >= maxlen) {
-    tracepoint(librados, rados_ioctx_get_pool_name_exit, -ERANGE, "");
-    return -ERANGE;
-  }
-  strcpy(s, pool_name.c_str());
-  int retval = pool_name.length();
-  tracepoint(librados, rados_ioctx_get_pool_name_exit, retval, s);
-  return retval;
-}
-
-// snaps
-
-extern "C" int rados_ioctx_snap_create(rados_ioctx_t io, const char *snapname)
-{
-  tracepoint(librados, rados_ioctx_snap_create_enter, io, snapname);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  int retval = ctx->snap_create(snapname);
-  tracepoint(librados, rados_ioctx_snap_create_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_ioctx_snap_remove(rados_ioctx_t io, const char *snapname)
-{
-  tracepoint(librados, rados_ioctx_snap_remove_enter, io, snapname);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  int retval = ctx->snap_remove(snapname);
-  tracepoint(librados, rados_ioctx_snap_remove_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_ioctx_snap_rollback(rados_ioctx_t io, const char *oid,
-                             const char *snapname)
-{
-  tracepoint(librados, rados_ioctx_snap_rollback_enter, io, oid, snapname);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  int retval = ctx->rollback(oid, snapname);
-  tracepoint(librados, rados_ioctx_snap_rollback_exit, retval);
-  return retval;
-}
-
-// Deprecated name kept for backward compatibility
-extern "C" int rados_rollback(rados_ioctx_t io, const char *oid,
-                             const char *snapname)
-{
-  return rados_ioctx_snap_rollback(io, oid, snapname);
-}
-
-extern "C" int rados_ioctx_selfmanaged_snap_create(rados_ioctx_t io,
-                                            uint64_t *snapid)
-{
-  tracepoint(librados, rados_ioctx_selfmanaged_snap_create_enter, io);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  int retval = ctx->selfmanaged_snap_create(snapid);
-  tracepoint(librados, rados_ioctx_selfmanaged_snap_create_exit, retval, *snapid);
-  return retval;
-}
-
-extern "C" void
-rados_aio_ioctx_selfmanaged_snap_create(rados_ioctx_t io,
-                                        rados_snap_t *snapid,
-                                        rados_completion_t completion)
-{
-  tracepoint(librados, rados_ioctx_selfmanaged_snap_create_enter, io);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  librados::AioCompletionImpl *c = (librados::AioCompletionImpl*)completion;
-  ctx->aio_selfmanaged_snap_create(snapid, c);
-  tracepoint(librados, rados_ioctx_selfmanaged_snap_create_exit, 0, 0);
-}
-
-extern "C" int rados_ioctx_selfmanaged_snap_remove(rados_ioctx_t io,
-                                            uint64_t snapid)
-{
-  tracepoint(librados, rados_ioctx_selfmanaged_snap_remove_enter, io, snapid);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  int retval = ctx->selfmanaged_snap_remove(snapid);
-  tracepoint(librados, rados_ioctx_selfmanaged_snap_remove_exit, retval);
-  return retval;
-}
-
-extern "C" void
-rados_aio_ioctx_selfmanaged_snap_remove(rados_ioctx_t io,
-                                        rados_snap_t snapid,
-                                        rados_completion_t completion)
-{
-  tracepoint(librados, rados_ioctx_selfmanaged_snap_remove_enter, io, snapid);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  librados::AioCompletionImpl *c = (librados::AioCompletionImpl*)completion;
-  ctx->aio_selfmanaged_snap_remove(snapid, c);
-  tracepoint(librados, rados_ioctx_selfmanaged_snap_remove_exit, 0);
-}
-
-extern "C" int rados_ioctx_selfmanaged_snap_rollback(rados_ioctx_t io,
-                                                    const char *oid,
-                                                    uint64_t snapid)
-{
-  tracepoint(librados, rados_ioctx_selfmanaged_snap_rollback_enter, io, oid, snapid);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  int retval = ctx->selfmanaged_snap_rollback_object(oid, ctx->snapc, snapid);
-  tracepoint(librados, rados_ioctx_selfmanaged_snap_rollback_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_ioctx_snap_list(rados_ioctx_t io, rados_snap_t *snaps,
-                                   int maxlen)
-{
-  tracepoint(librados, rados_ioctx_snap_list_enter, io, maxlen);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  vector<uint64_t> snapvec;
-  int r = ctx->snap_list(&snapvec);
-  if (r < 0) {
-    tracepoint(librados, rados_ioctx_snap_list_exit, r, snaps, 0);
-    return r;
-  }
-  if ((int)snapvec.size() <= maxlen) {
-    for (unsigned i=0; i<snapvec.size(); i++) {
-      snaps[i] = snapvec[i];
-    }
-    int retval = snapvec.size();
-    tracepoint(librados, rados_ioctx_snap_list_exit, retval, snaps, retval);
-    return retval;
-  }
-  int retval = -ERANGE;
-  tracepoint(librados, rados_ioctx_snap_list_exit, retval, snaps, 0);
-  return retval;
-}
-
-extern "C" int rados_ioctx_snap_lookup(rados_ioctx_t io, const char *name,
-                                     rados_snap_t *id)
-{
-  tracepoint(librados, rados_ioctx_snap_lookup_enter, io, name);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  int retval = ctx->snap_lookup(name, (uint64_t *)id);
-  tracepoint(librados, rados_ioctx_snap_lookup_exit, retval, *id);
-  return retval;
-}
-
-extern "C" int rados_ioctx_snap_get_name(rados_ioctx_t io, rados_snap_t id,
-                                       char *name, int maxlen)
-{
-  tracepoint(librados, rados_ioctx_snap_get_name_enter, io, id, maxlen);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  std::string sname;
-  int r = ctx->snap_get_name(id, &sname);
-  if (r < 0) {
-    tracepoint(librados, rados_ioctx_snap_get_name_exit, r, "");
-    return r;
-  }
-  if ((int)sname.length() >= maxlen) {
-    int retval = -ERANGE;
-    tracepoint(librados, rados_ioctx_snap_get_name_exit, retval, "");
-    return retval;
-  }
-  strncpy(name, sname.c_str(), maxlen);
-  tracepoint(librados, rados_ioctx_snap_get_name_exit, 0, name);
-  return 0;
-}
-
-extern "C" int rados_ioctx_snap_get_stamp(rados_ioctx_t io, rados_snap_t id, time_t *t)
-{
-  tracepoint(librados, rados_ioctx_snap_get_stamp_enter, io, id);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  int retval = ctx->snap_get_stamp(id, t);
-  tracepoint(librados, rados_ioctx_snap_get_stamp_exit, retval, *t);
-  return retval;
-}
-
-extern "C" int rados_cmpext(rados_ioctx_t io, const char *o,
-                           const char *cmp_buf, size_t cmp_len, uint64_t off)
-{
-  tracepoint(librados, rados_cmpext_enter, io, o, cmp_buf, cmp_len, off);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  int ret;
-  object_t oid(o);
-
-  bufferlist cmp_bl;
-  cmp_bl.append(cmp_buf, cmp_len);
-
-  ret = ctx->cmpext(oid, off, cmp_bl);
-  tracepoint(librados, rados_cmpext_exit, ret);
-
-  return ret;
-}
-
-extern "C" int rados_getxattr(rados_ioctx_t io, const char *o, const char *name,
-                             char *buf, size_t len)
-{
-  tracepoint(librados, rados_getxattr_enter, io, o, name, len);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  int ret;
-  object_t oid(o);
-  bufferlist bl;
-  bl.push_back(buffer::create_static(len, buf));
-  ret = ctx->getxattr(oid, name, bl);
-  if (ret >= 0) {
-    if (bl.length() > len) {
-      tracepoint(librados, rados_getxattr_exit, -ERANGE, buf, 0);
-      return -ERANGE;
-    }
-    if (!bl.is_provided_buffer(buf))
-      bl.copy(0, bl.length(), buf);
-    ret = bl.length();
-  }
-
-  tracepoint(librados, rados_getxattr_exit, ret, buf, ret);
-  return ret;
-}
-
-extern "C" int rados_getxattrs(rados_ioctx_t io, const char *oid,
-                              rados_xattrs_iter_t *iter)
-{
-  tracepoint(librados, rados_getxattrs_enter, io, oid);
-  librados::RadosXattrsIter *it = new librados::RadosXattrsIter();
-  if (!it) {
-    tracepoint(librados, rados_getxattrs_exit, -ENOMEM, NULL);
-    return -ENOMEM;
-  }
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  object_t obj(oid);
-  int ret = ctx->getxattrs(obj, it->attrset);
-  if (ret) {
-    delete it;
-    tracepoint(librados, rados_getxattrs_exit, ret, NULL);
-    return ret;
-  }
-  it->i = it->attrset.begin();
-
-  *iter = it;
-  tracepoint(librados, rados_getxattrs_exit, 0, *iter);
-  return 0;
-}
-
-extern "C" int rados_getxattrs_next(rados_xattrs_iter_t iter,
-                                   const char **name, const char **val, size_t *len)
-{
-  tracepoint(librados, rados_getxattrs_next_enter, iter);
-  librados::RadosXattrsIter *it = static_cast<librados::RadosXattrsIter*>(iter);
-  if (it->val) {
-    free(it->val);
-    it->val = NULL;
-  }
-  if (it->i == it->attrset.end()) {
-    *name = NULL;
-    *val = NULL;
-    *len = 0;
-    tracepoint(librados, rados_getxattrs_next_exit, 0, NULL, NULL, 0);
-    return 0;
-  }
-  const std::string &s(it->i->first);
-  *name = s.c_str();
-  bufferlist &bl(it->i->second);
-  size_t bl_len = bl.length();
-  if (!bl_len) {
-    // malloc(0) is not guaranteed to return a valid pointer
-    *val = (char *)NULL;
-  } else {
-    it->val = (char*)malloc(bl_len);
-    if (!it->val) {
-      tracepoint(librados, rados_getxattrs_next_exit, -ENOMEM, *name, NULL, 0);
-      return -ENOMEM;
-    }
-    memcpy(it->val, bl.c_str(), bl_len);
-    *val = it->val;
-  }
-  *len = bl_len;
-  ++it->i;
-  tracepoint(librados, rados_getxattrs_next_exit, 0, *name, *val, *len);
-  return 0;
-}
-
-extern "C" void rados_getxattrs_end(rados_xattrs_iter_t iter)
-{
-  tracepoint(librados, rados_getxattrs_end_enter, iter);
-  librados::RadosXattrsIter *it = static_cast<librados::RadosXattrsIter*>(iter);
-  delete it;
-  tracepoint(librados, rados_getxattrs_end_exit);
-}
-
-extern "C" int rados_setxattr(rados_ioctx_t io, const char *o, const char *name, const char *buf, size_t len)
-{
-  tracepoint(librados, rados_setxattr_enter, io, o, name, buf, len);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  object_t oid(o);
-  bufferlist bl;
-  bl.append(buf, len);
-  int retval = ctx->setxattr(oid, name, bl);
-  tracepoint(librados, rados_setxattr_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_rmxattr(rados_ioctx_t io, const char *o, const char *name)
-{
-  tracepoint(librados, rados_rmxattr_enter, io, o, name);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  object_t oid(o);
-  int retval = ctx->rmxattr(oid, name);
-  tracepoint(librados, rados_rmxattr_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_stat(rados_ioctx_t io, const char *o, uint64_t *psize, time_t *pmtime)
-{
-  tracepoint(librados, rados_stat_enter, io, o);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  object_t oid(o);
-  int retval = ctx->stat(oid, psize, pmtime);
-  tracepoint(librados, rados_stat_exit, retval, psize, pmtime);
-  return retval;
-}
-
-extern "C" int rados_tmap_update(rados_ioctx_t io, const char *o, const char *cmdbuf, size_t cmdbuflen)
-{
-  tracepoint(librados, rados_tmap_update_enter, io, o, cmdbuf, cmdbuflen);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  object_t oid(o);
-  bufferlist cmdbl;
-  cmdbl.append(cmdbuf, cmdbuflen);
-  int retval = ctx->tmap_update(oid, cmdbl);
-  tracepoint(librados, rados_tmap_update_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_tmap_put(rados_ioctx_t io, const char *o, const char *buf, size_t buflen)
-{
-  tracepoint(librados, rados_tmap_put_enter, io, o, buf, buflen);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  object_t oid(o);
-  bufferlist bl;
-  bl.append(buf, buflen);
-  int retval = ctx->tmap_put(oid, bl);
-  tracepoint(librados, rados_tmap_put_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_tmap_get(rados_ioctx_t io, const char *o, char *buf, size_t buflen)
-{
-  tracepoint(librados, rados_tmap_get_enter, io, o, buflen);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  object_t oid(o);
-  bufferlist bl;
-  int r = ctx->tmap_get(oid, bl);
-  if (r < 0) {
-    tracepoint(librados, rados_tmap_get_exit, r, buf, 0);
-    return r;
-  }
-  if (bl.length() > buflen) {
-    tracepoint(librados, rados_tmap_get_exit, -ERANGE, buf, 0);
-    return -ERANGE;
-  }
-  bl.copy(0, bl.length(), buf);
-  int retval = bl.length();
-  tracepoint(librados, rados_tmap_get_exit, retval, buf, retval);
-  return retval;
-}
-
-extern "C" int rados_tmap_to_omap(rados_ioctx_t io, const char *o, bool nullok)
-{
-  tracepoint(librados, rados_tmap_to_omap_enter, io, o, nullok);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  object_t oid(o);
-  int retval = ctx->tmap_to_omap(oid, nullok);
-  tracepoint(librados, rados_tmap_to_omap_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_exec(rados_ioctx_t io, const char *o, const char *cls, const char *method,
-                         const char *inbuf, size_t in_len, char *buf, size_t out_len)
-{
-  tracepoint(librados, rados_exec_enter, io, o, cls, method, inbuf, in_len, out_len);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  object_t oid(o);
-  bufferlist inbl, outbl;
-  int ret;
-  inbl.append(inbuf, in_len);
-  ret = ctx->exec(oid, cls, method, inbl, outbl);
-  if (ret >= 0) {
-    if (outbl.length()) {
-      if (outbl.length() > out_len) {
-       tracepoint(librados, rados_exec_exit, -ERANGE, buf, 0);
-       return -ERANGE;
-      }
-      outbl.copy(0, outbl.length(), buf);
-      ret = outbl.length();   // hrm :/
-    }
-  }
-  tracepoint(librados, rados_exec_exit, ret, buf, ret);
-  return ret;
-}
-
-extern "C" rados_object_list_cursor rados_object_list_begin(rados_ioctx_t io)
-{
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-
-  hobject_t *result = new hobject_t(ctx->objecter->enumerate_objects_begin());
-  return (rados_object_list_cursor)result;
-}
-
-extern "C" rados_object_list_cursor rados_object_list_end(rados_ioctx_t io)
-{
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-
-  hobject_t *result = new hobject_t(ctx->objecter->enumerate_objects_end());
-  return (rados_object_list_cursor)result;
-}
-
-extern "C" int rados_object_list_is_end(
-    rados_ioctx_t io, rados_object_list_cursor cur)
-{
-  hobject_t *hobj = (hobject_t*)cur;
-  return hobj->is_max();
-}
-
-extern "C" void rados_object_list_cursor_free(
-    rados_ioctx_t io, rados_object_list_cursor cur)
-{
-  hobject_t *hobj = (hobject_t*)cur;
-  delete hobj;
-}
-
-extern "C" int rados_object_list_cursor_cmp(
-    rados_ioctx_t io,
-    rados_object_list_cursor lhs_cur,
-    rados_object_list_cursor rhs_cur)
-{
-  hobject_t *lhs = (hobject_t*)lhs_cur;
-  hobject_t *rhs = (hobject_t*)rhs_cur;
-  return cmp(*lhs, *rhs);
-}
-
-extern "C" int rados_object_list(rados_ioctx_t io,
-    const rados_object_list_cursor start,
-    const rados_object_list_cursor finish,
-    const size_t result_item_count,
-    const char *filter_buf,
-    const size_t filter_buf_len,
-    rados_object_list_item *result_items,
-    rados_object_list_cursor *next)
-{
-  ceph_assert(next);
-
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-
-  // Zero out items so that they will be safe to free later
-  memset(result_items, 0, sizeof(rados_object_list_item) * result_item_count);
-
-  std::list<librados::ListObjectImpl> result;
-  hobject_t next_hash;
-
-  bufferlist filter_bl;
-  if (filter_buf != nullptr) {
-    filter_bl.append(filter_buf, filter_buf_len);
-  }
-
-  C_SaferCond cond;
-  ctx->objecter->enumerate_objects(
-      ctx->poolid,
-      ctx->oloc.nspace,
-      *((hobject_t*)start),
-      *((hobject_t*)finish),
-      result_item_count,
-      filter_bl,
-      &result,
-      &next_hash,
-      &cond);
-
-  hobject_t *next_hobj = (hobject_t*)(*next);
-  ceph_assert(next_hobj);
-
-  int r = cond.wait();
-  if (r < 0) {
-    *next_hobj = hobject_t::get_max();
-    return r;
-  }
-
-  ceph_assert(result.size() <= result_item_count);  // Don't overflow!
-
-  int k = 0;
-  for (std::list<librados::ListObjectImpl>::iterator i = result.begin();
-       i != result.end(); ++i) {
-    rados_object_list_item &item = result_items[k++];
-    do_out_buffer(i->oid, &item.oid, &item.oid_length);
-    do_out_buffer(i->nspace, &item.nspace, &item.nspace_length);
-    do_out_buffer(i->locator, &item.locator, &item.locator_length);
-  }
-
-  *next_hobj = next_hash;
-
-  return result.size();
-}
-
-extern "C" void rados_object_list_free(
-    const size_t result_size,
-    rados_object_list_item *results)
-{
-  ceph_assert(results);
-
-  for (unsigned int i = 0; i < result_size; ++i) {
-    rados_buffer_free(results[i].oid);
-    rados_buffer_free(results[i].locator);
-    rados_buffer_free(results[i].nspace);
-  }
-}
-
-/* list objects */
-
-extern "C" int rados_nobjects_list_open(rados_ioctx_t io, rados_list_ctx_t *listh)
-{
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-
-  tracepoint(librados, rados_nobjects_list_open_enter, io);
-
-  Objecter::NListContext *h = new Objecter::NListContext;
-  h->pool_id = ctx->poolid;
-  h->pool_snap_seq = ctx->snap_seq;
-  h->nspace = ctx->oloc.nspace;        // After dropping compatibility need nspace
-  *listh = (void *)new librados::ObjListCtx(ctx, h);
-  tracepoint(librados, rados_nobjects_list_open_exit, 0, *listh);
-  return 0;
-}
-
-extern "C" void rados_nobjects_list_close(rados_list_ctx_t h)
-{
-  tracepoint(librados, rados_nobjects_list_close_enter, h);
-  librados::ObjListCtx *lh = (librados::ObjListCtx *)h;
-  delete lh;
-  tracepoint(librados, rados_nobjects_list_close_exit);
-}
-
-extern "C" uint32_t rados_nobjects_list_seek(rados_list_ctx_t listctx,
-                                           uint32_t pos)
-{
-  librados::ObjListCtx *lh = (librados::ObjListCtx *)listctx;
-  tracepoint(librados, rados_nobjects_list_seek_enter, listctx, pos);
-  uint32_t r = lh->ctx->nlist_seek(lh->nlc, pos);
-  tracepoint(librados, rados_nobjects_list_seek_exit, r);
-  return r;
-}
-
-extern "C" uint32_t rados_nobjects_list_seek_cursor(rados_list_ctx_t listctx,
-                                                    rados_object_list_cursor cursor)
-{
-  librados::ObjListCtx *lh = (librados::ObjListCtx *)listctx;
-
-  tracepoint(librados, rados_nobjects_list_seek_cursor_enter, listctx);
-  uint32_t r = lh->ctx->nlist_seek(lh->nlc, cursor);
-  tracepoint(librados, rados_nobjects_list_seek_cursor_exit, r);
-  return r;
-}
-
-extern "C" int rados_nobjects_list_get_cursor(rados_list_ctx_t listctx,
-                                              rados_object_list_cursor *cursor)
-{
-  librados::ObjListCtx *lh = (librados::ObjListCtx *)listctx;
-
-  tracepoint(librados, rados_nobjects_list_get_cursor_enter, listctx);
-  *cursor = lh->ctx->nlist_get_cursor(lh->nlc);
-  tracepoint(librados, rados_nobjects_list_get_cursor_exit, 0);
-  return 0;
-}
-
-extern "C" uint32_t rados_nobjects_list_get_pg_hash_position(
-  rados_list_ctx_t listctx)
-{
-  librados::ObjListCtx *lh = (librados::ObjListCtx *)listctx;
-  tracepoint(librados, rados_nobjects_list_get_pg_hash_position_enter, listctx);
-  uint32_t retval = lh->nlc->get_pg_hash_position();
-  tracepoint(librados, rados_nobjects_list_get_pg_hash_position_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_nobjects_list_next(rados_list_ctx_t listctx, const char **entry, const char **key, const char **nspace)
-{
-  tracepoint(librados, rados_nobjects_list_next_enter, listctx);
-  librados::ObjListCtx *lh = (librados::ObjListCtx *)listctx;
-  Objecter::NListContext *h = lh->nlc;
-
-  // if the list is non-empty, this method has been called before
-  if (!h->list.empty())
-    // so let's kill the previously-returned object
-    h->list.pop_front();
-
-  if (h->list.empty()) {
-    int ret = lh->ctx->nlist(lh->nlc, RADOS_LIST_MAX_ENTRIES);
-    if (ret < 0) {
-      tracepoint(librados, rados_nobjects_list_next_exit, ret, NULL, NULL, NULL);
-      return ret;
-    }
-    if (h->list.empty()) {
-      tracepoint(librados, rados_nobjects_list_next_exit, -ENOENT, NULL, NULL, NULL);
-      return -ENOENT;
-    }
-  }
-
-  *entry = h->list.front().oid.c_str();
-
-  if (key) {
-    if (h->list.front().locator.size())
-      *key = h->list.front().locator.c_str();
-    else
-      *key = NULL;
-  }
-  if (nspace)
-    *nspace = h->list.front().nspace.c_str();
-  tracepoint(librados, rados_nobjects_list_next_exit, 0, *entry, key, nspace);
-  return 0;
-}
-
-
-/*
- * removed legacy v2 list objects stubs
- *
- * thse return -ENOTSUP where possible.
- */
-extern "C" int rados_objects_list_open(
-  rados_ioctx_t io,
-  rados_list_ctx_t *ctx)
-{
-  return -ENOTSUP;
-}
-
-extern "C" uint32_t rados_objects_list_get_pg_hash_position(
-  rados_list_ctx_t ctx)
-{
-  return 0;
-}
-
-extern "C" uint32_t rados_objects_list_seek(
-  rados_list_ctx_t ctx,
-  uint32_t pos)
-{
-  return 0;
-}
-
-extern "C" int rados_objects_list_next(
-  rados_list_ctx_t ctx,
-  const char **entry,
-  const char **key)
-{
-  return -ENOTSUP;
-}
-
-extern "C" void rados_objects_list_close(
-  rados_list_ctx_t ctx)
-{
-}
-
-
-// -------------------------
-// aio
-
-extern "C" int rados_aio_create_completion(void *cb_arg,
-                                          rados_callback_t cb_complete,
-                                          rados_callback_t cb_safe,
-                                          rados_completion_t *pc)
-{
-  tracepoint(librados, rados_aio_create_completion_enter, cb_arg, cb_complete, cb_safe);
-  librados::AioCompletionImpl *c = new librados::AioCompletionImpl;
-  if (cb_complete)
-    c->set_complete_callback(cb_arg, cb_complete);
-  if (cb_safe)
-    c->set_safe_callback(cb_arg, cb_safe);
-  *pc = c;
-  tracepoint(librados, rados_aio_create_completion_exit, 0, *pc);
-  return 0;
-}
-
-extern "C" int rados_aio_wait_for_complete(rados_completion_t c)
-{
-  tracepoint(librados, rados_aio_wait_for_complete_enter, c);
-  int retval = ((librados::AioCompletionImpl*)c)->wait_for_complete();
-  tracepoint(librados, rados_aio_wait_for_complete_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_aio_wait_for_safe(rados_completion_t c)
-{
-  tracepoint(librados, rados_aio_wait_for_safe_enter, c);
-  int retval = ((librados::AioCompletionImpl*)c)->wait_for_safe();
-  tracepoint(librados, rados_aio_wait_for_safe_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_aio_is_complete(rados_completion_t c)
-{
-  tracepoint(librados, rados_aio_is_complete_enter, c);
-  int retval = ((librados::AioCompletionImpl*)c)->is_complete();
-  tracepoint(librados, rados_aio_is_complete_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_aio_is_safe(rados_completion_t c)
-{
-  tracepoint(librados, rados_aio_is_safe_enter, c);
-  int retval = ((librados::AioCompletionImpl*)c)->is_safe();
-  tracepoint(librados, rados_aio_is_safe_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_aio_wait_for_complete_and_cb(rados_completion_t c)
-{
-  tracepoint(librados, rados_aio_wait_for_complete_and_cb_enter, c);
-  int retval = ((librados::AioCompletionImpl*)c)->wait_for_complete_and_cb();
-  tracepoint(librados, rados_aio_wait_for_complete_and_cb_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_aio_wait_for_safe_and_cb(rados_completion_t c)
-{
-  tracepoint(librados, rados_aio_wait_for_safe_and_cb_enter, c);
-  int retval = ((librados::AioCompletionImpl*)c)->wait_for_safe_and_cb();
-  tracepoint(librados, rados_aio_wait_for_safe_and_cb_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_aio_is_complete_and_cb(rados_completion_t c)
-{
-  tracepoint(librados, rados_aio_is_complete_and_cb_enter, c);
-  int retval = ((librados::AioCompletionImpl*)c)->is_complete_and_cb();
-  tracepoint(librados, rados_aio_is_complete_and_cb_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_aio_is_safe_and_cb(rados_completion_t c)
-{
-  tracepoint(librados, rados_aio_is_safe_and_cb_enter, c);
-  int retval = ((librados::AioCompletionImpl*)c)->is_safe_and_cb();
-  tracepoint(librados, rados_aio_is_safe_and_cb_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_aio_get_return_value(rados_completion_t c)
-{
-  tracepoint(librados, rados_aio_get_return_value_enter, c);
-  int retval = ((librados::AioCompletionImpl*)c)->get_return_value();
-  tracepoint(librados, rados_aio_get_return_value_exit, retval);
-  return retval;
-}
-
-extern "C" uint64_t rados_aio_get_version(rados_completion_t c)
-{
-  tracepoint(librados, rados_aio_get_version_enter, c);
-  uint64_t retval = ((librados::AioCompletionImpl*)c)->get_version();
-  tracepoint(librados, rados_aio_get_version_exit, retval);
-  return retval;
-}
-
-extern "C" void rados_aio_release(rados_completion_t c)
-{
-  tracepoint(librados, rados_aio_release_enter, c);
-  ((librados::AioCompletionImpl*)c)->put();
-  tracepoint(librados, rados_aio_release_exit);
-}
-
-extern "C" int rados_aio_read(rados_ioctx_t io, const char *o,
-                              rados_completion_t completion,
-                              char *buf, size_t len, uint64_t off)
-{
-  tracepoint(librados, rados_aio_read_enter, io, o, completion, len, off);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  object_t oid(o);
-  int retval = ctx->aio_read(oid, (librados::AioCompletionImpl*)completion,
-                      buf, len, off, ctx->snap_seq);
-  tracepoint(librados, rados_aio_read_exit, retval);
-  return retval;
-}
-
-#ifdef WITH_BLKIN
-extern "C" int rados_aio_read_traced(rados_ioctx_t io, const char *o,
-                                    rados_completion_t completion,
-                                    char *buf, size_t len, uint64_t off,
-                                    struct blkin_trace_info *info)
-{
-  tracepoint(librados, rados_aio_read_enter, io, o, completion, len, off);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  object_t oid(o);
-  int retval = ctx->aio_read(oid, (librados::AioCompletionImpl*)completion,
-                             buf, len, off, ctx->snap_seq, info);
-  tracepoint(librados, rados_aio_read_exit, retval);
-  return retval;
-}
-#endif
-
-extern "C" int rados_aio_write(rados_ioctx_t io, const char *o,
-                               rados_completion_t completion,
-                               const char *buf, size_t len, uint64_t off)
-{
-  tracepoint(librados, rados_aio_write_enter, io, o, completion, buf, len, off);
-  if (len > UINT_MAX/2)
-    return -E2BIG;
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  object_t oid(o);
-  bufferlist bl;
-  bl.append(buf, len);
-  int retval = ctx->aio_write(oid, (librados::AioCompletionImpl*)completion,
-                       bl, len, off);
-  tracepoint(librados, rados_aio_write_exit, retval);
-  return retval;
-}
-
-#ifdef WITH_BLKIN
-extern "C" int rados_aio_write_traced(rados_ioctx_t io, const char *o,
-                                      rados_completion_t completion,
-                                      const char *buf, size_t len, uint64_t off,
-                                      struct blkin_trace_info *info)
-{
-  tracepoint(librados, rados_aio_write_enter, io, o, completion, buf, len, off);
-  if (len > UINT_MAX/2)
-    return -E2BIG;
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  object_t oid(o);
-  bufferlist bl;
-  bl.append(buf, len);
-  int retval = ctx->aio_write(oid, (librados::AioCompletionImpl*)completion,
-                              bl, len, off, info);
-  tracepoint(librados, rados_aio_write_exit, retval);
-  return retval;
-}
-#endif
-
-extern "C" int rados_aio_append(rados_ioctx_t io, const char *o,
-                               rados_completion_t completion,
-                               const char *buf, size_t len)
-{
-  tracepoint(librados, rados_aio_append_enter, io, o, completion, buf, len);
-  if (len > UINT_MAX/2)
-    return -E2BIG;
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  object_t oid(o);
-  bufferlist bl;
-  bl.append(buf, len);
-  int retval = ctx->aio_append(oid, (librados::AioCompletionImpl*)completion,
-                        bl, len);
-  tracepoint(librados, rados_aio_append_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_aio_write_full(rados_ioctx_t io, const char *o,
-                                   rados_completion_t completion,
-                                   const char *buf, size_t len)
-{
-  tracepoint(librados, rados_aio_write_full_enter, io, o, completion, buf, len);
-  if (len > UINT_MAX/2)
-    return -E2BIG;
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  object_t oid(o);
-  bufferlist bl;
-  bl.append(buf, len);
-  int retval = ctx->aio_write_full(oid, (librados::AioCompletionImpl*)completion, bl);
-  tracepoint(librados, rados_aio_write_full_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_aio_writesame(rados_ioctx_t io, const char *o,
-                                  rados_completion_t completion,
-                                  const char *buf, size_t data_len,
-                                  size_t write_len, uint64_t off)
-{
-  tracepoint(librados, rados_aio_writesame_enter, io, o, completion, buf,
-                                               data_len, write_len, off);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  object_t oid(o);
-  bufferlist bl;
-  bl.append(buf, data_len);
-  int retval = ctx->aio_writesame(o, (librados::AioCompletionImpl*)completion,
-                                 bl, write_len, off);
-  tracepoint(librados, rados_aio_writesame_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_aio_remove(rados_ioctx_t io, const char *o,
-                               rados_completion_t completion)
-{
-  tracepoint(librados, rados_aio_remove_enter, io, o, completion);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  object_t oid(o);
-  int retval = ctx->aio_remove(oid, (librados::AioCompletionImpl*)completion);
-  tracepoint(librados, rados_aio_remove_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_aio_flush_async(rados_ioctx_t io,
-                                    rados_completion_t completion)
-{
-  tracepoint(librados, rados_aio_flush_async_enter, io, completion);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  ctx->flush_aio_writes_async((librados::AioCompletionImpl*)completion);
-  tracepoint(librados, rados_aio_flush_async_exit, 0);
-  return 0;
-}
-
-extern "C" int rados_aio_flush(rados_ioctx_t io)
-{
-  tracepoint(librados, rados_aio_flush_enter, io);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  ctx->flush_aio_writes();
-  tracepoint(librados, rados_aio_flush_exit, 0);
-  return 0;
-}
-
-struct AioGetxattrData {
-  AioGetxattrData(char* buf, rados_completion_t c, size_t l) :
-    user_buf(buf), len(l), user_completion((librados::AioCompletionImpl*)c) {}
-  bufferlist bl;
-  char* user_buf;
-  size_t len;
-  struct librados::C_AioCompleteAndSafe user_completion;
-};
-
-static void rados_aio_getxattr_complete(rados_completion_t c, void *arg) {
-  AioGetxattrData *cdata = reinterpret_cast<AioGetxattrData*>(arg);
-  int rc = rados_aio_get_return_value(c);
-  if (rc >= 0) {
-    if (cdata->bl.length() > cdata->len) {
-      rc = -ERANGE;
-    } else {
-      if (!cdata->bl.is_provided_buffer(cdata->user_buf))
-       cdata->bl.copy(0, cdata->bl.length(), cdata->user_buf);
-      rc = cdata->bl.length();
-    }
-  }
-  cdata->user_completion.finish(rc);
-  delete cdata;
-}
-
-extern "C" int rados_aio_getxattr(rados_ioctx_t io, const char *o,
-                                 rados_completion_t completion,
-                                 const char *name, char *buf, size_t len)
-{
-  tracepoint(librados, rados_aio_getxattr_enter, io, o, completion, name, len);
-  // create data object to be passed to async callback
-  AioGetxattrData *cdata = new AioGetxattrData(buf, completion, len);
-  if (!cdata) {
-    tracepoint(librados, rados_aio_getxattr_exit, -ENOMEM, NULL, 0);
-    return -ENOMEM;
-  }
-  cdata->bl.push_back(buffer::create_static(len, buf));
-  // create completion callback
-  librados::AioCompletionImpl *c = new librados::AioCompletionImpl;
-  c->set_complete_callback(cdata, rados_aio_getxattr_complete);
-  // call async getxattr of IoCtx
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  object_t oid(o);
-  int ret = ctx->aio_getxattr(oid, c, name, cdata->bl);
-  tracepoint(librados, rados_aio_getxattr_exit, ret, buf, ret);
-  return ret;
-}
-
-namespace {
-struct AioGetxattrsData {
-  AioGetxattrsData(rados_completion_t c, rados_xattrs_iter_t *_iter) :
-    iter(_iter), user_completion((librados::AioCompletionImpl*)c) {
-    it = new librados::RadosXattrsIter();
-  }
-  ~AioGetxattrsData() {
-    if (it) delete it;
-  }
-  librados::RadosXattrsIter *it;
-  rados_xattrs_iter_t *iter;
-  struct librados::C_AioCompleteAndSafe user_completion;
-};
-}
-
-static void rados_aio_getxattrs_complete(rados_completion_t c, void *arg) {
-  AioGetxattrsData *cdata = reinterpret_cast<AioGetxattrsData*>(arg);
-  int rc = rados_aio_get_return_value(c);
-  if (rc) {
-    cdata->user_completion.finish(rc);
-  } else {
-    cdata->it->i = cdata->it->attrset.begin();
-    *cdata->iter = cdata->it;
-    cdata->it = 0;
-    cdata->user_completion.finish(0);
-  }
-  delete cdata;
-}
-
-extern "C" int rados_aio_getxattrs(rados_ioctx_t io, const char *oid,
-                                  rados_completion_t completion,
-                                  rados_xattrs_iter_t *iter)
-{
-  tracepoint(librados, rados_aio_getxattrs_enter, io, oid, completion);
-  // create data object to be passed to async callback
-  AioGetxattrsData *cdata = new AioGetxattrsData(completion, iter);
-  if (!cdata) {
-    tracepoint(librados, rados_getxattrs_exit, -ENOMEM, NULL);
-    return -ENOMEM;
-  }
-  // create completion callback
-  librados::AioCompletionImpl *c = new librados::AioCompletionImpl;
-  c->set_complete_callback(cdata, rados_aio_getxattrs_complete);
-  // call async getxattrs of IoCtx
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  object_t obj(oid);
-  int ret = ctx->aio_getxattrs(obj, c, cdata->it->attrset);
-  tracepoint(librados, rados_aio_getxattrs_exit, ret, cdata->it);
-  return ret;
-}
-
-extern "C" int rados_aio_setxattr(rados_ioctx_t io, const char *o,
-                                 rados_completion_t completion,
-                                 const char *name, const char *buf, size_t len)
-{
-  tracepoint(librados, rados_aio_setxattr_enter, io, o, completion, name, buf, len);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  object_t oid(o);
-  bufferlist bl;
-  bl.append(buf, len);
-  int retval = ctx->aio_setxattr(oid, (librados::AioCompletionImpl*)completion, name, bl);
-  tracepoint(librados, rados_aio_setxattr_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_aio_rmxattr(rados_ioctx_t io, const char *o,
-                                rados_completion_t completion,
-                                const char *name)
-{
-  tracepoint(librados, rados_aio_rmxattr_enter, io, o, completion, name);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  object_t oid(o);
-  int retval = ctx->aio_rmxattr(oid, (librados::AioCompletionImpl*)completion, name);
-  tracepoint(librados, rados_aio_rmxattr_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_aio_stat(rados_ioctx_t io, const char *o,
-                             rados_completion_t completion,
-                             uint64_t *psize, time_t *pmtime)
-{
-  tracepoint(librados, rados_aio_stat_enter, io, o, completion);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  object_t oid(o);
-  int retval = ctx->aio_stat(oid, (librados::AioCompletionImpl*)completion,
-                      psize, pmtime);
-  tracepoint(librados, rados_aio_stat_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_aio_cmpext(rados_ioctx_t io, const char *o,
-                               rados_completion_t completion, const char *cmp_buf,
-                               size_t cmp_len, uint64_t off)
-{
-  tracepoint(librados, rados_aio_cmpext_enter, io, o, completion, cmp_buf,
-            cmp_len, off);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  object_t oid(o);
-  int retval = ctx->aio_cmpext(oid, (librados::AioCompletionImpl*)completion,
-                              cmp_buf, cmp_len, off);
-  tracepoint(librados, rados_aio_cmpext_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_aio_cancel(rados_ioctx_t io, rados_completion_t completion)
-{
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  return ctx->aio_cancel((librados::AioCompletionImpl*)completion);
-}
-
-extern "C" int rados_aio_exec(rados_ioctx_t io, const char *o,
-                             rados_completion_t completion,
-                             const char *cls, const char *method,
-                             const char *inbuf, size_t in_len,
-                             char *buf, size_t out_len)
-{
-  tracepoint(librados, rados_aio_exec_enter, io, o, completion);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  object_t oid(o);
-  bufferlist inbl;
-  inbl.append(inbuf, in_len);
-  int retval = ctx->aio_exec(oid, (librados::AioCompletionImpl*)completion,
-                      cls, method, inbl, buf, out_len);
-  tracepoint(librados, rados_aio_exec_exit, retval);
-  return retval;
-}
-
-struct C_WatchCB : public librados::WatchCtx {
-  rados_watchcb_t wcb;
-  void *arg;
-  C_WatchCB(rados_watchcb_t _wcb, void *_arg) : wcb(_wcb), arg(_arg) {}
-  void notify(uint8_t opcode, uint64_t ver, bufferlist& bl) override {
-    wcb(opcode, ver, arg);
-  }
-};
-
-extern "C" int rados_watch(rados_ioctx_t io, const char *o, uint64_t ver,
-                          uint64_t *handle,
-                          rados_watchcb_t watchcb, void *arg)
-{
-  tracepoint(librados, rados_watch_enter, io, o, ver, watchcb, arg);
-  uint64_t *cookie = handle;
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  object_t oid(o);
-  C_WatchCB *wc = new C_WatchCB(watchcb, arg);
-  int retval = ctx->watch(oid, cookie, wc, NULL, true);
-  tracepoint(librados, rados_watch_exit, retval, *handle);
-  return retval;
-}
-
-struct C_WatchCB2 : public librados::WatchCtx2 {
-  rados_watchcb2_t wcb;
-  rados_watcherrcb_t errcb;
-  void *arg;
-  C_WatchCB2(rados_watchcb2_t _wcb,
-            rados_watcherrcb_t _errcb,
-            void *_arg) : wcb(_wcb), errcb(_errcb), arg(_arg) {}
-  void handle_notify(uint64_t notify_id,
-                    uint64_t cookie,
-                    uint64_t notifier_gid,
-                    bufferlist& bl) override {
-    wcb(arg, notify_id, cookie, notifier_gid, bl.c_str(), bl.length());
-  }
-  void handle_error(uint64_t cookie, int err) override {
-    if (errcb)
-      errcb(arg, cookie, err);
-  }
-};
-
-extern "C" int rados_watch2(rados_ioctx_t io, const char *o, uint64_t *handle,
-                           rados_watchcb2_t watchcb,
-                           rados_watcherrcb_t watcherrcb,
-                           void *arg) {
-  return rados_watch3(io, o, handle, watchcb, watcherrcb, 0, arg);
-}
-
-extern "C" int rados_watch3(rados_ioctx_t io, const char *o, uint64_t *handle,
-                           rados_watchcb2_t watchcb,
-                           rados_watcherrcb_t watcherrcb,
-                           uint32_t timeout,
-                           void *arg)
-{
-  tracepoint(librados, rados_watch3_enter, io, o, handle, watchcb, timeout, arg);
-  int ret;
-  if (!watchcb || !o || !handle) {
-    ret = -EINVAL;
-  } else {
-    uint64_t *cookie = handle;
-    librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-    object_t oid(o);
-    C_WatchCB2 *wc = new C_WatchCB2(watchcb, watcherrcb, arg);
-    ret = ctx->watch(oid, cookie, NULL, wc, timeout, true);
-  }
-  tracepoint(librados, rados_watch3_exit, ret, handle ? *handle : 0);
-  return ret;
-}
-
-extern "C" int rados_aio_watch(rados_ioctx_t io, const char *o,
-                               rados_completion_t completion,
-                               uint64_t *handle,
-                               rados_watchcb2_t watchcb,
-                               rados_watcherrcb_t watcherrcb, void *arg) {
-  return rados_aio_watch2(io, o, completion, handle, watchcb, watcherrcb, 0, arg);
-}
-
-extern "C" int rados_aio_watch2(rados_ioctx_t io, const char *o,
-                                rados_completion_t completion,
-                                uint64_t *handle,
-                                rados_watchcb2_t watchcb,
-                                rados_watcherrcb_t watcherrcb,
-                                uint32_t timeout, void *arg)
-{
-  tracepoint(librados, rados_aio_watch2_enter, io, o, completion, handle, watchcb, timeout, arg);
-  int ret;
-  if (!completion || !watchcb || !o || !handle) {
-    ret = -EINVAL;
-  } else {
-    uint64_t *cookie = handle;
-    librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-    object_t oid(o);
-    librados::AioCompletionImpl *c =
-      reinterpret_cast<librados::AioCompletionImpl*>(completion);
-    C_WatchCB2 *wc = new C_WatchCB2(watchcb, watcherrcb, arg);
-    ret = ctx->aio_watch(oid, c, cookie, NULL, wc, timeout, true);
-  }
-  tracepoint(librados, rados_aio_watch2_exit, ret, handle ? *handle : 0);
-  return ret;
-}
-
-
-extern "C" int rados_unwatch(rados_ioctx_t io, const char *o, uint64_t handle)
-{
-  tracepoint(librados, rados_unwatch_enter, io, o, handle);
-  uint64_t cookie = handle;
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  int retval = ctx->unwatch(cookie);
-  tracepoint(librados, rados_unwatch_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_unwatch2(rados_ioctx_t io, uint64_t handle)
-{
-  tracepoint(librados, rados_unwatch2_enter, io, handle);
-  uint64_t cookie = handle;
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  int retval = ctx->unwatch(cookie);
-  tracepoint(librados, rados_unwatch2_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_aio_unwatch(rados_ioctx_t io, uint64_t handle,
-                                 rados_completion_t completion)
-{
-  tracepoint(librados, rados_aio_unwatch_enter, io, handle, completion);
-  uint64_t cookie = handle;
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  librados::AioCompletionImpl *c =
-    reinterpret_cast<librados::AioCompletionImpl*>(completion);
-  int retval = ctx->aio_unwatch(cookie, c);
-  tracepoint(librados, rados_aio_unwatch_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_watch_check(rados_ioctx_t io, uint64_t handle)
-{
-  tracepoint(librados, rados_watch_check_enter, io, handle);
-  uint64_t cookie = handle;
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  int retval = ctx->watch_check(cookie);
-  tracepoint(librados, rados_watch_check_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_notify(rados_ioctx_t io, const char *o,
-                           uint64_t ver, const char *buf, int buf_len)
-{
-  tracepoint(librados, rados_notify_enter, io, o, ver, buf, buf_len);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  object_t oid(o);
-  bufferlist bl;
-  if (buf) {
-    bufferptr p = buffer::create(buf_len);
-    memcpy(p.c_str(), buf, buf_len);
-    bl.push_back(p);
-  }
-  int retval = ctx->notify(oid, bl, 0, NULL, NULL, NULL);
-  tracepoint(librados, rados_notify_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_notify2(rados_ioctx_t io, const char *o,
-                            const char *buf, int buf_len,
-                            uint64_t timeout_ms,
-                            char **reply_buffer,
-                            size_t *reply_buffer_len)
-{
-  tracepoint(librados, rados_notify2_enter, io, o, buf, buf_len, timeout_ms);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  object_t oid(o);
-  bufferlist bl;
-  if (buf) {
-    bufferptr p = buffer::create(buf_len);
-    memcpy(p.c_str(), buf, buf_len);
-    bl.push_back(p);
-  }
-  int ret = ctx->notify(oid, bl, timeout_ms, NULL, reply_buffer, reply_buffer_len);
-  tracepoint(librados, rados_notify2_exit, ret);
-  return ret;
-}
-
-extern "C" int rados_aio_notify(rados_ioctx_t io, const char *o,
-                                rados_completion_t completion,
-                                const char *buf, int buf_len,
-                                uint64_t timeout_ms, char **reply_buffer,
-                                size_t *reply_buffer_len)
-{
-  tracepoint(librados, rados_aio_notify_enter, io, o, completion, buf, buf_len,
-             timeout_ms);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  object_t oid(o);
-  bufferlist bl;
-  if (buf) {
-    bl.push_back(buffer::copy(buf, buf_len));
-  }
-  librados::AioCompletionImpl *c =
-    reinterpret_cast<librados::AioCompletionImpl*>(completion);
-  int ret = ctx->aio_notify(oid, c, bl, timeout_ms, NULL, reply_buffer,
-                            reply_buffer_len);
-  tracepoint(librados, rados_aio_notify_exit, ret);
-  return ret;
-}
-
-extern "C" int rados_notify_ack(rados_ioctx_t io, const char *o,
-                               uint64_t notify_id, uint64_t handle,
-                               const char *buf, int buf_len)
-{
-  tracepoint(librados, rados_notify_ack_enter, io, o, notify_id, handle, buf, buf_len);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  object_t oid(o);
-  bufferlist bl;
-  if (buf) {
-    bufferptr p = buffer::create(buf_len);
-    memcpy(p.c_str(), buf, buf_len);
-    bl.push_back(p);
-  }
-  ctx->notify_ack(oid, notify_id, handle, bl);
-  tracepoint(librados, rados_notify_ack_exit, 0);
-  return 0;
-}
-
-extern "C" int rados_watch_flush(rados_t cluster)
-{
-  tracepoint(librados, rados_watch_flush_enter, cluster);
-  librados::RadosClient *client = (librados::RadosClient *)cluster;
-  int retval = client->watch_flush();
-  tracepoint(librados, rados_watch_flush_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_aio_watch_flush(rados_t cluster, rados_completion_t completion)
-{
-  tracepoint(librados, rados_aio_watch_flush_enter, cluster, completion);
-  librados::RadosClient *client = (librados::RadosClient *)cluster;
-  librados::AioCompletionImpl *c = (librados::AioCompletionImpl*)completion;
-  int retval = client->async_watch_flush(c);
-  tracepoint(librados, rados_aio_watch_flush_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_set_alloc_hint(rados_ioctx_t io, const char *o,
-                                    uint64_t expected_object_size,
-                                    uint64_t expected_write_size)
-{
-  tracepoint(librados, rados_set_alloc_hint_enter, io, o, expected_object_size, expected_write_size);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  object_t oid(o);
-  int retval = ctx->set_alloc_hint(oid, expected_object_size,
-                                  expected_write_size, 0);
-  tracepoint(librados, rados_set_alloc_hint_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_set_alloc_hint2(rados_ioctx_t io, const char *o,
-                                    uint64_t expected_object_size,
-                                    uint64_t expected_write_size,
-                                    uint32_t flags)
-{
-  tracepoint(librados, rados_set_alloc_hint2_enter, io, o, expected_object_size, expected_write_size, flags);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  object_t oid(o);
-  int retval = ctx->set_alloc_hint(oid, expected_object_size,
-                                  expected_write_size, flags);
-  tracepoint(librados, rados_set_alloc_hint2_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_lock_exclusive(rados_ioctx_t io, const char * o,
-                         const char * name, const char * cookie,
-                         const char * desc, struct timeval * duration,
-                         uint8_t flags)
-{
-  tracepoint(librados, rados_lock_exclusive_enter, io, o, name, cookie, desc, duration, flags);
-  librados::IoCtx ctx;
-  librados::IoCtx::from_rados_ioctx_t(io, ctx);
-
-  int retval = ctx.lock_exclusive(o, name, cookie, desc, duration, flags);
-  tracepoint(librados, rados_lock_exclusive_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_lock_shared(rados_ioctx_t io, const char * o,
-                         const char * name, const char * cookie,
-                         const char * tag, const char * desc,
-                         struct timeval * duration, uint8_t flags)
-{
-  tracepoint(librados, rados_lock_shared_enter, io, o, name, cookie, tag, desc, duration, flags);
-  librados::IoCtx ctx;
-  librados::IoCtx::from_rados_ioctx_t(io, ctx);
-
-  int retval = ctx.lock_shared(o, name, cookie, tag, desc, duration, flags);
-  tracepoint(librados, rados_lock_shared_exit, retval);
-  return retval;
-}
-extern "C" int rados_unlock(rados_ioctx_t io, const char *o, const char *name,
-                           const char *cookie)
-{
-  tracepoint(librados, rados_unlock_enter, io, o, name, cookie);
-  librados::IoCtx ctx;
-  librados::IoCtx::from_rados_ioctx_t(io, ctx);
-
-  int retval = ctx.unlock(o, name, cookie);
-  tracepoint(librados, rados_unlock_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_aio_unlock(rados_ioctx_t io, const char *o, const char *name,
-                               const char *cookie, rados_completion_t completion)
-{
-  tracepoint(librados, rados_aio_unlock_enter, io, o, name, cookie, completion);
-  librados::IoCtx ctx;
-  librados::IoCtx::from_rados_ioctx_t(io, ctx);
-  librados::AioCompletionImpl *comp = (librados::AioCompletionImpl*)completion;
-  librados::AioCompletion c(comp);
-  int retval = ctx.aio_unlock(o, name, cookie, &c);
-  tracepoint(librados, rados_aio_unlock_exit, retval);
-  return retval;
-}
-
-extern "C" ssize_t rados_list_lockers(rados_ioctx_t io, const char *o,
-                                     const char *name, int *exclusive,
-                                     char *tag, size_t *tag_len,
-                                     char *clients, size_t *clients_len,
-                                     char *cookies, size_t *cookies_len,
-                                     char *addrs, size_t *addrs_len)
-{
-  tracepoint(librados, rados_list_lockers_enter, io, o, name, *tag_len, *clients_len, *cookies_len, *addrs_len);
-  librados::IoCtx ctx;
-  librados::IoCtx::from_rados_ioctx_t(io, ctx);
-  std::string name_str = name;
-  std::string oid = o;
-  std::string tag_str;
-  int tmp_exclusive;
-  std::list<librados::locker_t> lockers;
-  int r = ctx.list_lockers(oid, name_str, &tmp_exclusive, &tag_str, &lockers);
-  if (r < 0) {
-    tracepoint(librados, rados_list_lockers_exit, r, *exclusive, "", *tag_len, *clients_len, *cookies_len, *addrs_len);
-         return r;
-  }
-
-  size_t clients_total = 0;
-  size_t cookies_total = 0;
-  size_t addrs_total = 0;
-  list<librados::locker_t>::const_iterator it;
-  for (it = lockers.begin(); it != lockers.end(); ++it) {
-    clients_total += it->client.length() + 1;
-    cookies_total += it->cookie.length() + 1;
-    addrs_total += it->address.length() + 1;
-  }
-
-  bool too_short = ((clients_total > *clients_len) ||
-                    (cookies_total > *cookies_len) ||
-                    (addrs_total > *addrs_len) ||
-                    (tag_str.length() + 1 > *tag_len));
-  *clients_len = clients_total;
-  *cookies_len = cookies_total;
-  *addrs_len = addrs_total;
-  *tag_len = tag_str.length() + 1;
-  if (too_short) {
-    tracepoint(librados, rados_list_lockers_exit, -ERANGE, *exclusive, "", *tag_len, *clients_len, *cookies_len, *addrs_len);
-    return -ERANGE;
-  }
-
-  strcpy(tag, tag_str.c_str());
-  char *clients_p = clients;
-  char *cookies_p = cookies;
-  char *addrs_p = addrs;
-  for (it = lockers.begin(); it != lockers.end(); ++it) {
-    strcpy(clients_p, it->client.c_str());
-    strcpy(cookies_p, it->cookie.c_str());
-    strcpy(addrs_p, it->address.c_str());
-    tracepoint(librados, rados_list_lockers_locker, clients_p, cookies_p, addrs_p);
-    clients_p += it->client.length() + 1;
-    cookies_p += it->cookie.length() + 1;
-    addrs_p += it->address.length() + 1;
-  }
-  if (tmp_exclusive)
-    *exclusive = 1;
-  else
-    *exclusive = 0;
-
-  int retval = lockers.size();
-  tracepoint(librados, rados_list_lockers_exit, retval, *exclusive, tag, *tag_len, *clients_len, *cookies_len, *addrs_len);
-  return retval;
-}
-
-extern "C" int rados_break_lock(rados_ioctx_t io, const char *o,
-                               const char *name, const char *client,
-                               const char *cookie)
-{
-  tracepoint(librados, rados_break_lock_enter, io, o, name, client, cookie);
-  librados::IoCtx ctx;
-  librados::IoCtx::from_rados_ioctx_t(io, ctx);
-
-  int retval = ctx.break_lock(o, name, client, cookie);
-  tracepoint(librados, rados_break_lock_exit, retval);
-  return retval;
-}
-
-extern "C" rados_write_op_t rados_create_write_op()
-{
-  tracepoint(librados, rados_create_write_op_enter);
-  rados_write_op_t retval = new (std::nothrow)::ObjectOperation;
-  tracepoint(librados, rados_create_write_op_exit, retval);
-  return retval;
-}
-
-extern "C" void rados_release_write_op(rados_write_op_t write_op)
-{
-  tracepoint(librados, rados_release_write_op_enter, write_op);
-  delete (::ObjectOperation*)write_op;
-  tracepoint(librados, rados_release_write_op_exit);
-}
-
-extern "C" void rados_write_op_set_flags(rados_write_op_t write_op, int flags)
-{
-  tracepoint(librados, rados_write_op_set_flags_enter, write_op, flags);
-  set_op_flags((::ObjectOperation *)write_op, flags);
-  tracepoint(librados, rados_write_op_set_flags_exit);
-}
-
-extern "C" void rados_write_op_assert_version(rados_write_op_t write_op, uint64_t ver)
-{
-  tracepoint(librados, rados_write_op_assert_version_enter, write_op, ver);
-  ((::ObjectOperation *)write_op)->assert_version(ver);
-  tracepoint(librados, rados_write_op_assert_version_exit);
-}
-
-extern "C" void rados_write_op_assert_exists(rados_write_op_t write_op)
-{
-  tracepoint(librados, rados_write_op_assert_exists_enter, write_op);
-  ((::ObjectOperation *)write_op)->stat(NULL, (ceph::real_time *)NULL, NULL);
-  tracepoint(librados, rados_write_op_assert_exists_exit);
-}
-
-extern "C" void rados_write_op_cmpext(rados_write_op_t write_op,
-                                     const char *cmp_buf,
-                                     size_t cmp_len,
-                                     uint64_t off,
-                                     int *prval)
-{
-  tracepoint(librados, rados_write_op_cmpext_enter, write_op, cmp_buf,
-            cmp_len, off, prval);
-  ((::ObjectOperation *)write_op)->cmpext(off, cmp_len, cmp_buf, prval);
-  tracepoint(librados, rados_write_op_cmpext_exit);
-}
-
-extern "C" void rados_write_op_cmpxattr(rados_write_op_t write_op,
-                                       const char *name,
-                                      uint8_t comparison_operator,
-                                      const char *value,
-                                      size_t value_len)
-{
-  tracepoint(librados, rados_write_op_cmpxattr_enter, write_op, name, comparison_operator, value, value_len);
-  bufferlist bl;
-  bl.append(value, value_len);
-  ((::ObjectOperation *)write_op)->cmpxattr(name,
-                                           comparison_operator,
-                                           CEPH_OSD_CMPXATTR_MODE_STRING,
-                                           bl);
-  tracepoint(librados, rados_write_op_cmpxattr_exit);
-}
-
-static void rados_c_omap_cmp(ObjectOperation *op,
-                            const char *key,
-                            uint8_t comparison_operator,
-                            const char *val,
-                             size_t key_len,
-                            size_t val_len,
-                            int *prval)
-{
-  bufferlist bl;
-  bl.append(val, val_len);
-  std::map<std::string, pair<bufferlist, int> > assertions;
-  string lkey = string(key, key_len);
-
-  assertions[lkey] = 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)
-{
-  tracepoint(librados, rados_write_op_omap_cmp_enter, write_op, key, comparison_operator, val, val_len, prval);
-  rados_c_omap_cmp((::ObjectOperation *)write_op, key, comparison_operator,
-                   val, strlen(key), val_len, prval);
-  tracepoint(librados, rados_write_op_omap_cmp_exit);
-}
-
-extern "C" void rados_write_op_omap_cmp2(rados_write_op_t write_op,
-                                        const char *key,
-                                        uint8_t comparison_operator,
-                                        const char *val,
-                                        size_t key_len,
-                                        size_t val_len,
-                                        int *prval)
-{
-  tracepoint(librados, rados_write_op_omap_cmp_enter, write_op, key, comparison_operator, val, val_len, prval);
-  rados_c_omap_cmp((::ObjectOperation *)write_op, key, comparison_operator,
-                   val, key_len, val_len, prval);
-  tracepoint(librados, rados_write_op_omap_cmp_exit);
-}
-
-extern "C" void rados_write_op_setxattr(rados_write_op_t write_op,
-                                       const char *name,
-                                      const char *value,
-                                      size_t value_len)
-{
-  tracepoint(librados, rados_write_op_setxattr_enter, write_op, name, value, value_len);
-  bufferlist bl;
-  bl.append(value, value_len);
-  ((::ObjectOperation *)write_op)->setxattr(name, bl);
-  tracepoint(librados, rados_write_op_setxattr_exit);
-}
-
-extern "C" void rados_write_op_rmxattr(rados_write_op_t write_op,
-                                       const char *name)
-{
-  tracepoint(librados, rados_write_op_rmxattr_enter, write_op, name);
-  ((::ObjectOperation *)write_op)->rmxattr(name);
-  tracepoint(librados, rados_write_op_rmxattr_exit);
-}
-
-extern "C" void rados_write_op_create(rados_write_op_t write_op,
-                                      int exclusive,
-                                     const char* category) // unused
-{
-  tracepoint(librados, rados_write_op_create_enter, write_op, exclusive);
-  ::ObjectOperation *oo = (::ObjectOperation *) write_op;
-  oo->create(!!exclusive);
-  tracepoint(librados, rados_write_op_create_exit);
-}
-
-extern "C" void rados_write_op_write(rados_write_op_t write_op,
-                                    const char *buffer,
-                                    size_t len,
-                                     uint64_t offset)
-{
-  tracepoint(librados, rados_write_op_write_enter, write_op, buffer, len, offset);
-  bufferlist bl;
-  bl.append(buffer,len);
-  ((::ObjectOperation *)write_op)->write(offset, bl);
-  tracepoint(librados, rados_write_op_write_exit);
-}
-
-extern "C" void rados_write_op_write_full(rados_write_op_t write_op,
-                                         const char *buffer,
-                                         size_t len)
-{
-  tracepoint(librados, rados_write_op_write_full_enter, write_op, buffer, len);
-  bufferlist bl;
-  bl.append(buffer,len);
-  ((::ObjectOperation *)write_op)->write_full(bl);
-  tracepoint(librados, rados_write_op_write_full_exit);
-}
-
-extern "C" void rados_write_op_writesame(rados_write_op_t write_op,
-                                        const char *buffer,
-                                        size_t data_len,
-                                        size_t write_len,
-                                        uint64_t offset)
-{
-  tracepoint(librados, rados_write_op_writesame_enter, write_op, buffer, data_len, write_len, offset);
-  bufferlist bl;
-  bl.append(buffer, data_len);
-  ((::ObjectOperation *)write_op)->writesame(offset, write_len, bl);
-  tracepoint(librados, rados_write_op_writesame_exit);
-}
-
-extern "C" void rados_write_op_append(rados_write_op_t write_op,
-                                     const char *buffer,
-                                     size_t len)
-{
-  tracepoint(librados, rados_write_op_append_enter, write_op, buffer, len);
-  bufferlist bl;
-  bl.append(buffer,len);
-  ((::ObjectOperation *)write_op)->append(bl);
-  tracepoint(librados, rados_write_op_append_exit);
-}
-
-extern "C" void rados_write_op_remove(rados_write_op_t write_op)
-{
-  tracepoint(librados, rados_write_op_remove_enter, write_op);
-  ((::ObjectOperation *)write_op)->remove();
-  tracepoint(librados, rados_write_op_remove_exit);
-}
-
-extern "C" void rados_write_op_truncate(rados_write_op_t write_op,
-                                       uint64_t offset)
-{
-  tracepoint(librados, rados_write_op_truncate_enter, write_op, offset);
-  ((::ObjectOperation *)write_op)->truncate(offset);
-  tracepoint(librados, rados_write_op_truncate_exit);
-}
-
-extern "C" void rados_write_op_zero(rados_write_op_t write_op,
-                                   uint64_t offset,
-                                   uint64_t len)
-{
-  tracepoint(librados, rados_write_op_zero_enter, write_op, offset, len);
-  ((::ObjectOperation *)write_op)->zero(offset, len);
-  tracepoint(librados, rados_write_op_zero_exit);
-}
-
-extern "C" void rados_write_op_exec(rados_write_op_t write_op,
-                                   const char *cls,
-                                   const char *method,
-                                   const char *in_buf,
-                                   size_t in_len,
-                                   int *prval)
-{
-  tracepoint(librados, rados_write_op_exec_enter, write_op, cls, method, in_buf, in_len, prval);
-  bufferlist inbl;
-  inbl.append(in_buf, in_len);
-  ((::ObjectOperation *)write_op)->call(cls, method, inbl, NULL, NULL, prval);
-  tracepoint(librados, rados_write_op_exec_exit);
-}
-
-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)
-{
-  tracepoint(librados, rados_write_op_omap_set_enter, write_op, num);
-  std::map<std::string, bufferlist> entries;
-  for (size_t i = 0; i < num; ++i) {
-    tracepoint(librados, rados_write_op_omap_set_entry, keys[i], vals[i], lens[i]);
-    bufferlist bl(lens[i]);
-    bl.append(vals[i], lens[i]);
-    entries[keys[i]] = bl;
-  }
-  ((::ObjectOperation *)write_op)->omap_set(entries);
-  tracepoint(librados, rados_write_op_omap_set_exit);
-}
-
-extern "C" void rados_write_op_omap_set2(rados_write_op_t write_op,
-                                        char const* const* keys,
-                                        char const* const* vals,
-                                        const size_t *key_lens,
-                                        const size_t *val_lens,
-                                        size_t num)
-{
-  tracepoint(librados, rados_write_op_omap_set_enter, write_op, num);
-  std::map<std::string, bufferlist> entries;
-  for (size_t i = 0; i < num; ++i) {
-    bufferlist bl(val_lens[i]);
-    bl.append(vals[i], val_lens[i]);
-    string key(keys[i], key_lens[i]);
-    entries[key] = bl;
-  }
-  ((::ObjectOperation *)write_op)->omap_set(entries);
-  tracepoint(librados, rados_write_op_omap_set_exit);
-}
-
-extern "C" void rados_write_op_omap_rm_keys(rados_write_op_t write_op,
-                                            char const* const* keys,
-                                            size_t keys_len)
-{
-  tracepoint(librados, rados_write_op_omap_rm_keys_enter, write_op, keys_len);
-  for(size_t i = 0; i < keys_len; i++) {
-    tracepoint(librados, rados_write_op_omap_rm_keys_entry, keys[i]);
-  }
-  std::set<std::string> to_remove(keys, keys + keys_len);
-  ((::ObjectOperation *)write_op)->omap_rm_keys(to_remove);
-  tracepoint(librados, rados_write_op_omap_rm_keys_exit);
-}
-
-extern "C" void rados_write_op_omap_rm_keys2(rados_write_op_t write_op,
-                                            char const* const* keys,
-                                            const size_t* key_lens,
-                                            size_t keys_len)
-{
-  tracepoint(librados, rados_write_op_omap_rm_keys_enter, write_op, keys_len);
-  std::set<std::string> to_remove;
-  for(size_t i = 0; i < keys_len; i++) {
-    to_remove.emplace(keys[i], key_lens[i]);
-  }
-  ((::ObjectOperation *)write_op)->omap_rm_keys(to_remove);
-  tracepoint(librados, rados_write_op_omap_rm_keys_exit);
-}
-
-extern "C" void rados_write_op_omap_clear(rados_write_op_t write_op)
-{
-  tracepoint(librados, rados_write_op_omap_clear_enter, write_op);
-  ((::ObjectOperation *)write_op)->omap_clear();
-  tracepoint(librados, rados_write_op_omap_clear_exit);
-}
-
-extern "C" void rados_write_op_set_alloc_hint(rados_write_op_t write_op,
-                                            uint64_t expected_object_size,
-                                            uint64_t expected_write_size)
-{
-  tracepoint(librados, rados_write_op_set_alloc_hint_enter, write_op, expected_object_size, expected_write_size);
-  ((::ObjectOperation *)write_op)->set_alloc_hint(expected_object_size,
-                                                  expected_write_size, 0);
-  tracepoint(librados, rados_write_op_set_alloc_hint_exit);
-}
-
-extern "C" void rados_write_op_set_alloc_hint2(rados_write_op_t write_op,
-                                              uint64_t expected_object_size,
-                                              uint64_t expected_write_size,
-                                              uint32_t flags)
-{
-  tracepoint(librados, rados_write_op_set_alloc_hint2_enter, write_op, expected_object_size, expected_write_size, flags);
-  ((::ObjectOperation *)write_op)->set_alloc_hint(expected_object_size,
-                                                  expected_write_size,
-                                                 flags);
-  tracepoint(librados, rados_write_op_set_alloc_hint2_exit);
-}
-
-extern "C" int rados_write_op_operate(rados_write_op_t write_op,
-                                      rados_ioctx_t io,
-                                      const char *oid,
-                                     time_t *mtime,
-                                     int flags)
-{
-  tracepoint(librados, rados_write_op_operate_enter, write_op, io, oid, mtime, flags);
-  object_t obj(oid);
-  ::ObjectOperation *oo = (::ObjectOperation *) write_op;
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-
-  ceph::real_time *prt = NULL;
-  ceph::real_time rt;
-
-  if (mtime) {
-    rt = ceph::real_clock::from_time_t(*mtime);
-    prt = &rt;
-  }
-
-  int retval = ctx->operate(obj, oo, prt, translate_flags(flags));
-  tracepoint(librados, rados_write_op_operate_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_write_op_operate2(rados_write_op_t write_op,
-                                       rados_ioctx_t io,
-                                       const char *oid,
-                                       struct timespec *ts,
-                                       int flags)
-{
-  tracepoint(librados, rados_write_op_operate2_enter, write_op, io, oid, ts, flags);
-  object_t obj(oid);
-  ::ObjectOperation *oo = (::ObjectOperation *) write_op;
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-
-  ceph::real_time *prt = NULL;
-  ceph::real_time rt;
-
-  if (ts) {
-    rt = ceph::real_clock::from_timespec(*ts);
-    prt = &rt;
-  }
-
-  int retval = ctx->operate(obj, oo, prt, translate_flags(flags));
-  tracepoint(librados, rados_write_op_operate_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_aio_write_op_operate(rados_write_op_t write_op,
-                                         rados_ioctx_t io,
-                                         rados_completion_t completion,
-                                         const char *oid,
-                                         time_t *mtime,
-                                         int flags)
-{
-  tracepoint(librados, rados_aio_write_op_operate_enter, write_op, io, completion, oid, mtime, flags);
-  object_t obj(oid);
-  ::ObjectOperation *oo = (::ObjectOperation *) write_op;
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  librados::AioCompletionImpl *c = (librados::AioCompletionImpl*)completion;
-  int retval = ctx->aio_operate(obj, oo, c, ctx->snapc, translate_flags(flags));
-  tracepoint(librados, rados_aio_write_op_operate_exit, retval);
-  return retval;
-}
-
-extern "C" rados_read_op_t rados_create_read_op()
-{
-  tracepoint(librados, rados_create_read_op_enter);
-  rados_read_op_t retval = new (std::nothrow)::ObjectOperation;
-  tracepoint(librados, rados_create_read_op_exit, retval);
-  return retval;
-}
-
-extern "C" void rados_release_read_op(rados_read_op_t read_op)
-{
-  tracepoint(librados, rados_release_read_op_enter, read_op);
-  delete (::ObjectOperation *)read_op;
-  tracepoint(librados, rados_release_read_op_exit);
-}
-
-extern "C" void rados_read_op_set_flags(rados_read_op_t read_op, int flags)
-{
-  tracepoint(librados, rados_read_op_set_flags_enter, read_op, flags);
-  set_op_flags((::ObjectOperation *)read_op, flags);
-  tracepoint(librados, rados_read_op_set_flags_exit);
-}
-
-extern "C" void rados_read_op_assert_version(rados_read_op_t read_op, uint64_t ver)
-{
-  tracepoint(librados, rados_read_op_assert_version_enter, read_op, ver);
-  ((::ObjectOperation *)read_op)->assert_version(ver);
-  tracepoint(librados, rados_read_op_assert_version_exit);
-}
-
-extern "C" void rados_read_op_assert_exists(rados_read_op_t read_op)
-{
-  tracepoint(librados, rados_read_op_assert_exists_enter, read_op);
-  ((::ObjectOperation *)read_op)->stat(NULL, (ceph::real_time *)NULL, NULL);
-  tracepoint(librados, rados_read_op_assert_exists_exit);
-}
-
-extern "C" void rados_read_op_cmpext(rados_read_op_t read_op,
-                                    const char *cmp_buf,
-                                    size_t cmp_len,
-                                    uint64_t off,
-                                    int *prval)
-{
-  tracepoint(librados, rados_read_op_cmpext_enter, read_op, cmp_buf,
-            cmp_len, off, prval);
-  ((::ObjectOperation *)read_op)->cmpext(off, cmp_len, cmp_buf, prval);
-  tracepoint(librados, rados_read_op_cmpext_exit);
-}
-
-extern "C" void rados_read_op_cmpxattr(rados_read_op_t read_op,
-                                      const char *name,
-                                      uint8_t comparison_operator,
-                                      const char *value,
-                                      size_t value_len)
-{
-  tracepoint(librados, rados_read_op_cmpxattr_enter, read_op, name, comparison_operator, value, value_len);
-  bufferlist bl;
-  bl.append(value, value_len);
-  ((::ObjectOperation *)read_op)->cmpxattr(name,
-                                          comparison_operator,
-                                          CEPH_OSD_CMPXATTR_MODE_STRING,
-                                          bl);
-  tracepoint(librados, rados_read_op_cmpxattr_exit);
-}
-
-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)
-{
-  tracepoint(librados, rados_read_op_omap_cmp_enter, read_op, key, comparison_operator, val, val_len, prval);
-  rados_c_omap_cmp((::ObjectOperation *)read_op, key, comparison_operator,
-                   val,  strlen(key), val_len, prval);
-  tracepoint(librados, rados_read_op_omap_cmp_exit);
-}
-
-extern "C" void rados_read_op_omap_cmp2(rados_read_op_t read_op,
-                                       const char *key,
-                                       uint8_t comparison_operator,
-                                       const char *val,
-                                       size_t key_len,
-                                       size_t val_len,
-                                       int *prval)
-{
-  tracepoint(librados, rados_read_op_omap_cmp_enter, read_op, key, comparison_operator, val, val_len, prval);
-  rados_c_omap_cmp((::ObjectOperation *)read_op, key, comparison_operator,
-                   val, key_len, val_len, prval);
-  tracepoint(librados, rados_read_op_omap_cmp_exit);
-}
-
-extern "C" void rados_read_op_stat(rados_read_op_t read_op,
-                                  uint64_t *psize,
-                                  time_t *pmtime,
-                                  int *prval)
-{
-  tracepoint(librados, rados_read_op_stat_enter, read_op, psize, pmtime, prval);
-  ((::ObjectOperation *)read_op)->stat(psize, pmtime, prval);
-  tracepoint(librados, rados_read_op_stat_exit);
-}
-
-class C_bl_to_buf : public Context {
-  char *out_buf;
-  size_t out_len;
-  size_t *bytes_read;
-  int *prval;
-public:
-  bufferlist out_bl;
-  C_bl_to_buf(char *out_buf,
-             size_t out_len,
-             size_t *bytes_read,
-             int *prval) : out_buf(out_buf), out_len(out_len),
-                           bytes_read(bytes_read), prval(prval) {}
-  void finish(int r) override {
-    if (out_bl.length() > out_len) {
-      if (prval)
-       *prval = -ERANGE;
-      if (bytes_read)
-       *bytes_read = 0;
-      return;
-    }
-    if (bytes_read)
-      *bytes_read = out_bl.length();
-    if (out_buf && !out_bl.is_provided_buffer(out_buf))
-      out_bl.copy(0, out_bl.length(), out_buf);
-  }
-};
-
-extern "C" void rados_read_op_read(rados_read_op_t read_op,
-                                  uint64_t offset,
-                                  size_t len,
-                                  char *buf,
-                                  size_t *bytes_read,
-                                  int *prval)
-{
-  tracepoint(librados, rados_read_op_read_enter, read_op, offset, len, buf, bytes_read, prval);
-  C_bl_to_buf *ctx = new C_bl_to_buf(buf, len, bytes_read, prval);
-  ctx->out_bl.push_back(buffer::create_static(len, buf));
-  ((::ObjectOperation *)read_op)->read(offset, len, &ctx->out_bl, prval, ctx);
-  tracepoint(librados, rados_read_op_read_exit);
-}
-
-extern "C" void rados_read_op_checksum(rados_read_op_t read_op,
-                                       rados_checksum_type_t type,
-                                       const char *init_value,
-                                       size_t init_value_len,
-                                       uint64_t offset, size_t len,
-                                       size_t chunk_size, char *pchecksum,
-                                      size_t checksum_len, int *prval)
-{
-  tracepoint(librados, rados_read_op_checksum_enter, read_op, type, init_value,
-            init_value_len, offset, len, chunk_size);
-  bufferlist init_value_bl;
-  init_value_bl.append(init_value, init_value_len);
-
-  C_bl_to_buf *ctx = nullptr;
-  if (pchecksum != nullptr) {
-    ctx = new C_bl_to_buf(pchecksum, checksum_len, nullptr, prval);
-  }
-  ((::ObjectOperation *)read_op)->checksum(get_checksum_op_type(type),
-                                          init_value_bl, offset, len,
-                                          chunk_size,
-                                          (ctx ? &ctx->out_bl : nullptr),
-                                          prval, ctx);
-  tracepoint(librados, rados_read_op_checksum_exit);
-}
-
-class C_out_buffer : public Context {
-  char **out_buf;
-  size_t *out_len;
-public:
-  bufferlist out_bl;
-  C_out_buffer(char **out_buf, size_t *out_len) : out_buf(out_buf),
-                                                 out_len(out_len) {}
-  void finish(int r) override {
-    // ignore r since we don't know the meaning of return values
-    // from custom class methods
-    do_out_buffer(out_bl, out_buf, out_len);
-  }
-};
-
-extern "C" void rados_read_op_exec(rados_read_op_t read_op,
-                                  const char *cls,
-                                  const char *method,
-                                  const char *in_buf,
-                                  size_t in_len,
-                                  char **out_buf,
-                                  size_t *out_len,
-                                  int *prval)
-{
-  tracepoint(librados, rados_read_op_exec_enter, read_op, cls, method, in_buf, in_len, out_buf, out_len, prval);
-  bufferlist inbl;
-  inbl.append(in_buf, in_len);
-  C_out_buffer *ctx = new C_out_buffer(out_buf, out_len);
-  ((::ObjectOperation *)read_op)->call(cls, method, inbl, &ctx->out_bl, ctx,
-                                      prval);
-  tracepoint(librados, rados_read_op_exec_exit);
-}
-
-extern "C" void rados_read_op_exec_user_buf(rados_read_op_t read_op,
-                                           const char *cls,
-                                           const char *method,
-                                           const char *in_buf,
-                                           size_t in_len,
-                                           char *out_buf,
-                                           size_t out_len,
-                                           size_t *used_len,
-                                           int *prval)
-{
-  tracepoint(librados, rados_read_op_exec_user_buf_enter, read_op, cls, method, in_buf, in_len, out_buf, out_len, used_len, prval);
-  C_bl_to_buf *ctx = new C_bl_to_buf(out_buf, out_len, used_len, prval);
-  bufferlist inbl;
-  inbl.append(in_buf, in_len);
-  ((::ObjectOperation *)read_op)->call(cls, method, inbl, &ctx->out_bl, ctx,
-                                      prval);
-  tracepoint(librados, rados_read_op_exec_user_buf_exit);
-}
-
-struct RadosOmapIter {
-  std::map<std::string, bufferlist> values;
-  std::map<std::string, bufferlist>::iterator i;
-};
-
-class C_OmapIter : public Context {
-  RadosOmapIter *iter;
-public:
-  explicit C_OmapIter(RadosOmapIter *iter) : iter(iter) {}
-  void finish(int r) override {
-    iter->i = iter->values.begin();
-  }
-};
-
-class C_XattrsIter : public Context {
-  librados::RadosXattrsIter *iter;
-public:
-  explicit C_XattrsIter(librados::RadosXattrsIter *iter) : iter(iter) {}
-  void finish(int r) override {
-    iter->i = iter->attrset.begin();
-  }
-};
-
-extern "C" void rados_read_op_getxattrs(rados_read_op_t read_op,
-                                       rados_xattrs_iter_t *iter,
-                                       int *prval)
-{
-  tracepoint(librados, rados_read_op_getxattrs_enter, read_op, prval);
-  librados::RadosXattrsIter *xattrs_iter = new librados::RadosXattrsIter;
-  ((::ObjectOperation *)read_op)->getxattrs(&xattrs_iter->attrset, prval);
-  ((::ObjectOperation *)read_op)->add_handler(new C_XattrsIter(xattrs_iter));
-  *iter = xattrs_iter;
-  tracepoint(librados, rados_read_op_getxattrs_exit, *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)
-{
-  tracepoint(librados, rados_read_op_omap_get_vals_enter, read_op, start_after, filter_prefix, max_return, 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,
-    nullptr,
-    prval);
-  ((::ObjectOperation *)read_op)->add_handler(new C_OmapIter(omap_iter));
-  *iter = omap_iter;
-  tracepoint(librados, rados_read_op_omap_get_vals_exit, *iter);
-}
-
-extern "C" void rados_read_op_omap_get_vals2(rados_read_op_t read_op,
-                                            const char *start_after,
-                                            const char *filter_prefix,
-                                            uint64_t max_return,
-                                            rados_omap_iter_t *iter,
-                                            unsigned char *pmore,
-                                            int *prval)
-{
-  tracepoint(librados, rados_read_op_omap_get_vals_enter, read_op, start_after, filter_prefix, max_return, 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,
-    (bool*)pmore,
-    prval);
-  ((::ObjectOperation *)read_op)->add_handler(new C_OmapIter(omap_iter));
-  *iter = omap_iter;
-  tracepoint(librados, rados_read_op_omap_get_vals_exit, *iter);
-}
-
-struct C_OmapKeysIter : public Context {
-  RadosOmapIter *iter;
-  std::set<std::string> keys;
-  explicit C_OmapKeysIter(RadosOmapIter *iter) : iter(iter) {}
-  void finish(int r) override {
-    // 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)
-{
-  tracepoint(librados, rados_read_op_omap_get_keys_enter, read_op, start_after, max_return, 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, nullptr, prval);
-  ((::ObjectOperation *)read_op)->add_handler(ctx);
-  *iter = omap_iter;
-  tracepoint(librados, rados_read_op_omap_get_keys_exit, *iter);
-}
-
-extern "C" void rados_read_op_omap_get_keys2(rados_read_op_t read_op,
-                                            const char *start_after,
-                                            uint64_t max_return,
-                                            rados_omap_iter_t *iter,
-                                            unsigned char *pmore,
-                                            int *prval)
-{
-  tracepoint(librados, rados_read_op_omap_get_keys_enter, read_op, start_after, max_return, 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,
-    (bool*)pmore, prval);
-  ((::ObjectOperation *)read_op)->add_handler(ctx);
-  *iter = omap_iter;
-  tracepoint(librados, rados_read_op_omap_get_keys_exit, *iter);
-}
-
-void internal_rados_read_op_omap_get_vals_by_keys(rados_read_op_t read_op,
-                                                    set<string>& to_get,
-                                                    rados_omap_iter_t *iter,
-                                                    int *prval)
-{
-  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" 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)
-{
-  tracepoint(librados, rados_read_op_omap_get_vals_by_keys_enter, read_op, keys, keys_len, iter, prval);
-  std::set<std::string> to_get(keys, keys + keys_len);
-  internal_rados_read_op_omap_get_vals_by_keys(read_op, to_get, iter, prval);
-  tracepoint(librados, rados_read_op_omap_get_vals_by_keys_exit, *iter);
-}
-
-extern "C" void rados_read_op_omap_get_vals_by_keys2(rados_read_op_t read_op,
-                                                    char const* const* keys,
-                                                    size_t num_keys,
-                                                    const size_t* key_lens,
-                                                    rados_omap_iter_t *iter,
-                                                    int *prval)
-{
-  tracepoint(librados, rados_read_op_omap_get_vals_by_keys_enter, read_op, keys, num_keys, iter, prval);
-  std::set<std::string> to_get;
-  for (size_t i = 0; i < num_keys; i++) {
-    to_get.emplace(keys[i], key_lens[i]);
-  }
-  internal_rados_read_op_omap_get_vals_by_keys(read_op, to_get, iter, prval);
-  tracepoint(librados, rados_read_op_omap_get_vals_by_keys_exit, *iter);
-}
-
-extern "C" int rados_omap_get_next(rados_omap_iter_t iter,
-                                   char **key,
-                                   char **val,
-                                   size_t *len)
-{
-  return rados_omap_get_next2(iter, key, val, nullptr, len);
-}
-
-extern "C" int rados_omap_get_next2(rados_omap_iter_t iter,
-                                   char **key,
-                                   char **val,
-                                   size_t *key_len,
-                                   size_t *val_len)
-{
-  tracepoint(librados, rados_omap_get_next_enter, iter);
-  RadosOmapIter *it = static_cast<RadosOmapIter *>(iter);
-  if (it->i == it->values.end()) {
-    if (key)
-      *key = NULL;
-    if (val)
-      *val = NULL;
-    if (key_len)
-      *key_len = 0;
-    if (val_len)
-      *val_len = 0;
-    tracepoint(librados, rados_omap_get_next_exit, 0, key, val, val_len);
-    return 0;
-  }
-  if (key)
-    *key = (char*)it->i->first.c_str();
-  if (val)
-    *val = it->i->second.c_str();
-  if (key_len)
-    *key_len = it->i->first.length();
-  if (val_len)
-    *val_len = it->i->second.length();
-  ++it->i;
-  tracepoint(librados, rados_omap_get_next_exit, 0, key, val, val_len);
-  return 0;
-}
-
-extern "C" unsigned int rados_omap_iter_size(rados_omap_iter_t iter)
-{
-  RadosOmapIter *it = static_cast<RadosOmapIter *>(iter);
-  return it->values.size();
-}
-
-extern "C" void rados_omap_get_end(rados_omap_iter_t iter)
-{
-  tracepoint(librados, rados_omap_get_end_enter, iter);
-  RadosOmapIter *it = static_cast<RadosOmapIter *>(iter);
-  delete it;
-  tracepoint(librados, rados_omap_get_end_exit);
-}
-
-extern "C" int rados_read_op_operate(rados_read_op_t read_op,
-                                    rados_ioctx_t io,
-                                    const char *oid,
-                                    int flags)
-{
-  tracepoint(librados, rados_read_op_operate_enter, read_op, io, oid, flags);
-  object_t obj(oid);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  int retval = ctx->operate_read(obj, (::ObjectOperation *)read_op, NULL,
-                                translate_flags(flags));
-  tracepoint(librados, rados_read_op_operate_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_aio_read_op_operate(rados_read_op_t read_op,
-                                        rados_ioctx_t io,
-                                        rados_completion_t completion,
-                                        const char *oid,
-                                        int flags)
-{
-  tracepoint(librados, rados_aio_read_op_operate_enter, read_op, io, completion, oid, flags);
-  object_t obj(oid);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  librados::AioCompletionImpl *c = (librados::AioCompletionImpl*)completion;
-  int retval = ctx->aio_operate_read(obj, (::ObjectOperation *)read_op,
-                                    c, translate_flags(flags), NULL);
-  tracepoint(librados, rados_aio_read_op_operate_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_cache_pin(rados_ioctx_t io, const char *o)
-{
-  tracepoint(librados, rados_cache_pin_enter, io, o);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  object_t oid(o);
-  int retval = ctx->cache_pin(oid);
-  tracepoint(librados, rados_cache_pin_exit, retval);
-  return retval;
-}
-
-extern "C" int rados_cache_unpin(rados_ioctx_t io, const char *o)
-{
-  tracepoint(librados, rados_cache_unpin_enter, io, o);
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-  object_t oid(o);
-  int retval = ctx->cache_unpin(oid);
-  tracepoint(librados, rados_cache_unpin_exit, retval);
-  return retval;
-}
-
-
-///////////////////////////// ListObject //////////////////////////////
-librados::ListObject::ListObject() : impl(NULL)
-{
-}
-
-librados::ListObject::ListObject(librados::ListObjectImpl *i): impl(i)
-{
-}
-
-librados::ListObject::ListObject(const ListObject& rhs)
-{
-  if (rhs.impl == NULL) {
-    impl = NULL;
-    return;
-  }
-  impl = new ListObjectImpl();
-  *impl = *(rhs.impl);
-}
-
-librados::ListObject& librados::ListObject::operator=(const ListObject& rhs)
-{
-  if (rhs.impl == NULL) {
-    delete impl;
-    impl = NULL;
-    return *this;
-  }
-  if (impl == NULL)
-    impl = new ListObjectImpl();
-  *impl = *(rhs.impl);
-  return *this;
-}
-
-librados::ListObject::~ListObject()
-{
-  if (impl)
-    delete impl;
-  impl = NULL;
-}
-
-const std::string& librados::ListObject::get_nspace() const
-{
-  return impl->get_nspace();
-}
-
-const std::string& librados::ListObject::get_oid() const
-{
-  return impl->get_oid();
-}
-
-const std::string& librados::ListObject::get_locator() const
-{
-  return impl->get_locator();
-}
-
-std::ostream& librados::operator<<(std::ostream& out, const librados::ListObject& lop)
-{
-  out << *(lop.impl);
-  return out;
-}
-
-CEPH_RADOS_API void rados_object_list_slice(
-    rados_ioctx_t io,
-    const rados_object_list_cursor start,
-    const rados_object_list_cursor finish,
-    const size_t n,
-    const size_t m,
-    rados_object_list_cursor *split_start,
-    rados_object_list_cursor *split_finish)
-{
-  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
-
-  ceph_assert(split_start);
-  ceph_assert(split_finish);
-  hobject_t *split_start_hobj = (hobject_t*)(*split_start);
-  hobject_t *split_finish_hobj = (hobject_t*)(*split_finish);
-  ceph_assert(split_start_hobj);
-  ceph_assert(split_finish_hobj);
-  hobject_t *start_hobj = (hobject_t*)(start);
-  hobject_t *finish_hobj = (hobject_t*)(finish);
-
-  ctx->object_list_slice(
-      *start_hobj,
-      *finish_hobj,
-      n,
-      m,
-      split_start_hobj,
-      split_finish_hobj);
-}
-
-librados::ObjectCursor::ObjectCursor()
-{
-  c_cursor = (rados_object_list_cursor)new hobject_t();
-}
-
-librados::ObjectCursor::~ObjectCursor()
-{
-  hobject_t *h = (hobject_t *)c_cursor;
-  delete h;
-}
-
-librados::ObjectCursor::ObjectCursor(rados_object_list_cursor c)
-{
-  if (!c) {
-    c_cursor = nullptr;
-  } else {
-    c_cursor = (rados_object_list_cursor)new hobject_t(*(hobject_t *)c);
-  }
-}
-
-librados::ObjectCursor& librados::ObjectCursor::operator=(const librados::ObjectCursor& rhs)
-{
-  if (rhs.c_cursor != nullptr) {
-    hobject_t *h = (hobject_t*)rhs.c_cursor;
-    c_cursor = (rados_object_list_cursor)(new hobject_t(*h));
-  } else {
-    c_cursor = nullptr;
-  }
-  return *this;
-}
-
-bool librados::ObjectCursor::operator<(const librados::ObjectCursor &rhs) const
-{
-  const hobject_t lhs_hobj = (c_cursor == nullptr) ? hobject_t() : *((hobject_t*)c_cursor);
-  const hobject_t rhs_hobj = (rhs.c_cursor == nullptr) ? hobject_t() : *((hobject_t*)(rhs.c_cursor));
-  return lhs_hobj < rhs_hobj;
-}
-
-bool librados::ObjectCursor::operator==(const librados::ObjectCursor &rhs) const
-{
-  const hobject_t lhs_hobj = (c_cursor == nullptr) ? hobject_t() : *((hobject_t*)c_cursor);
-  const hobject_t rhs_hobj = (rhs.c_cursor == nullptr) ? hobject_t() : *((hobject_t*)(rhs.c_cursor));
-  return cmp(lhs_hobj, rhs_hobj) == 0;
-}
-librados::ObjectCursor::ObjectCursor(const librados::ObjectCursor &rhs)
-{
-  *this = rhs;
-}
-
-librados::ObjectCursor librados::IoCtx::object_list_begin()
-{
-  hobject_t *h = new hobject_t(io_ctx_impl->objecter->enumerate_objects_begin());
-  ObjectCursor oc;
-  oc.set((rados_object_list_cursor)h);
-  return oc;
-}
-
-
-librados::ObjectCursor librados::IoCtx::object_list_end()
-{
-  hobject_t *h = new hobject_t(io_ctx_impl->objecter->enumerate_objects_end());
-  librados::ObjectCursor oc;
-  oc.set((rados_object_list_cursor)h);
-  return oc;
-}
-
-
-void librados::ObjectCursor::set(rados_object_list_cursor c)
-{
-  delete (hobject_t*)c_cursor;
-  c_cursor = c;
-}
-
-string librados::ObjectCursor::to_str() const
-{
-  stringstream ss;
-  ss << *(hobject_t *)c_cursor;
-  return ss.str();
-}
-
-bool librados::ObjectCursor::from_str(const string& s)
-{
-  if (s.empty()) {
-    *(hobject_t *)c_cursor = hobject_t();
-    return true;
-  }
-  return ((hobject_t *)c_cursor)->parse(s);
-}
-
-CEPH_RADOS_API std::ostream& librados::operator<<(std::ostream& os, const librados::ObjectCursor& oc)
-{
-  if (oc.c_cursor) {
-    os << *(hobject_t *)oc.c_cursor;
-  } else {
-    os << hobject_t();
-  }
-  return os;
-}
-
-bool librados::IoCtx::object_list_is_end(const ObjectCursor &oc)
-{
-  hobject_t *h = (hobject_t *)oc.c_cursor;
-  return h->is_max();
-}
-
-int librados::IoCtx::object_list(const ObjectCursor &start,
-                const ObjectCursor &finish,
-                const size_t result_item_count,
-                const bufferlist &filter,
-                std::vector<ObjectItem> *result,
-                ObjectCursor *next)
-{
-  ceph_assert(result != nullptr);
-  ceph_assert(next != nullptr);
-  result->clear();
-
-  C_SaferCond cond;
-  hobject_t next_hash;
-  std::list<librados::ListObjectImpl> obj_result;
-  io_ctx_impl->objecter->enumerate_objects(
-      io_ctx_impl->poolid,
-      io_ctx_impl->oloc.nspace,
-      *((hobject_t*)start.c_cursor),
-      *((hobject_t*)finish.c_cursor),
-      result_item_count,
-      filter,
-      &obj_result,
-      &next_hash,
-      &cond);
-
-  int r = cond.wait();
-  if (r < 0) {
-    next->set((rados_object_list_cursor)(new hobject_t(hobject_t::get_max())));
-    return r;
-  }
-
-  next->set((rados_object_list_cursor)(new hobject_t(next_hash)));
-
-  for (std::list<librados::ListObjectImpl>::iterator i = obj_result.begin();
-       i != obj_result.end(); ++i) {
-    ObjectItem oi;
-    oi.oid = i->oid;
-    oi.nspace = i->nspace;
-    oi.locator = i->locator;
-    result->push_back(oi);
-  }
-
-  return obj_result.size();
-}
-
-void librados::IoCtx::object_list_slice(
-    const ObjectCursor start,
-    const ObjectCursor finish,
-    const size_t n,
-    const size_t m,
-    ObjectCursor *split_start,
-    ObjectCursor *split_finish)
-{
-  ceph_assert(split_start != nullptr);
-  ceph_assert(split_finish != nullptr);
-
-  io_ctx_impl->object_list_slice(
-      *((hobject_t*)(start.c_cursor)),
-      *((hobject_t*)(finish.c_cursor)),
-      n,
-      m,
-      (hobject_t*)(split_start->c_cursor),
-      (hobject_t*)(split_finish->c_cursor));
-}
-
-int librados::IoCtx::application_enable(const std::string& app_name,
-                                        bool force)
-{
-  return io_ctx_impl->application_enable(app_name, force);
-}
-
-int librados::IoCtx::application_enable_async(const std::string& app_name,
-                                              bool force,
-                                              PoolAsyncCompletion *c)
-{
-  io_ctx_impl->application_enable_async(app_name, force, c->pc);
-  return 0;
-}
-
-int librados::IoCtx::application_list(std::set<std::string> *app_names)
-{
-  return io_ctx_impl->application_list(app_names);
-}
-
-int librados::IoCtx::application_metadata_get(const std::string& app_name,
-                                              const std::string &key,
-                                              std::string* value)
-{
-  return io_ctx_impl->application_metadata_get(app_name, key, value);
-}
-
-int librados::IoCtx::application_metadata_set(const std::string& app_name,
-                                              const std::string &key,
-                                              const std::string& value)
-{
-  return io_ctx_impl->application_metadata_set(app_name, key, value);
-}
-
-int librados::IoCtx::application_metadata_remove(const std::string& app_name,
-                                                 const std::string &key)
-{
-  return io_ctx_impl->application_metadata_remove(app_name, key);
-}
-
-int librados::IoCtx::application_metadata_list(const std::string& app_name,
-                                               std::map<std::string, std::string> *values)
-{
-  return io_ctx_impl->application_metadata_list(app_name, values);
-}
-
-
diff --git a/src/librados/librados_c.cc b/src/librados/librados_c.cc
new file mode 100644 (file)
index 0000000..d6f0c22
--- /dev/null
@@ -0,0 +1,3737 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <limits.h>
+
+#include "common/config.h"
+#include "common/errno.h"
+#include "common/ceph_argparse.h"
+#include "common/ceph_json.h"
+#include "common/common_init.h"
+#include "common/TracepointProvider.h"
+#include "common/hobject.h"
+#include "include/rados/librados.h"
+#include "include/types.h"
+#include <include/stringify.h>
+
+#include "librados/AioCompletionImpl.h"
+#include "librados/IoCtxImpl.h"
+#include "librados/PoolAsyncCompletionImpl.h"
+#include "librados/RadosClient.h"
+#include "librados/RadosXattrIter.h"
+#include "librados/ListObjectImpl.h"
+#include "librados/librados_util.h"
+#include <cls/lock/cls_lock_client.h>
+
+#include <string>
+#include <map>
+#include <set>
+#include <vector>
+#include <list>
+#include <stdexcept>
+
+#ifdef WITH_LTTNG
+#define TRACEPOINT_DEFINE
+#define TRACEPOINT_PROBE_DYNAMIC_LINKAGE
+#include "tracing/librados.h"
+#undef TRACEPOINT_PROBE_DYNAMIC_LINKAGE
+#undef TRACEPOINT_DEFINE
+#else
+#define tracepoint(...)
+#endif
+
+using std::string;
+using std::map;
+using std::set;
+using std::vector;
+using std::list;
+using std::runtime_error;
+
+#define dout_subsys ceph_subsys_rados
+#undef dout_prefix
+#define dout_prefix *_dout << "librados: "
+
+#define RADOS_LIST_MAX_ENTRIES 1024
+
+static TracepointProvider::Traits tracepoint_traits("librados_tp.so", "rados_tracing");
+
+/*
+ * Structure of this file
+ *
+ * RadosClient and the related classes are the internal implementation of librados.
+ * Above that layer sits the C API, found in include/rados/librados.h, and
+ * the C++ API, found in include/rados/librados.hpp
+ *
+ * The C++ API sometimes implements things in terms of the C API.
+ * Both the C++ and C API rely on RadosClient.
+ *
+ * Visually:
+ * +--------------------------------------+
+ * |             C++ API                  |
+ * +--------------------+                 |
+ * |       C API        |                 |
+ * +--------------------+-----------------+
+ * |          RadosClient                 |
+ * +--------------------------------------+
+ */
+
+///////////////////////////// C API //////////////////////////////
+
+static CephContext *rados_create_cct(const char * const clustername,
+                                     CephInitParameters *iparams)
+{
+  // missing things compared to global_init:
+  // g_ceph_context, g_conf, g_lockdep, signal handlers
+  CephContext *cct = common_preinit(*iparams, CODE_ENVIRONMENT_LIBRARY, 0);
+  if (clustername)
+    cct->_conf->cluster = clustername;
+  cct->_conf.parse_env(); // environment variables override
+  cct->_conf.apply_changes(nullptr);
+
+  TracepointProvider::initialize<tracepoint_traits>(cct);
+  return cct;
+}
+
+extern "C" int rados_create(rados_t *pcluster, const char * const id)
+{
+  CephInitParameters iparams(CEPH_ENTITY_TYPE_CLIENT);
+  if (id) {
+    iparams.name.set(CEPH_ENTITY_TYPE_CLIENT, id);
+  }
+  CephContext *cct = rados_create_cct("", &iparams);
+
+  tracepoint(librados, rados_create_enter, id);
+  *pcluster = reinterpret_cast<rados_t>(new librados::RadosClient(cct));
+  tracepoint(librados, rados_create_exit, 0, *pcluster);
+
+  cct->put();
+  return 0;
+}
+
+// as above, but
+// 1) don't assume 'client.'; name is a full type.id namestr
+// 2) allow setting clustername
+// 3) flags is for future expansion (maybe some of the global_init()
+//    behavior is appropriate for some consumers of librados, for instance)
+
+extern "C" int rados_create2(rados_t *pcluster, const char *const clustername,
+                            const char * const name, uint64_t flags)
+{
+  // client is assumed, but from_str will override
+  int retval = 0;
+  CephInitParameters iparams(CEPH_ENTITY_TYPE_CLIENT);
+  if (!name || !iparams.name.from_str(name)) {
+    retval = -EINVAL;
+  }
+
+  CephContext *cct = rados_create_cct(clustername, &iparams);
+  tracepoint(librados, rados_create2_enter, clustername, name, flags);
+  if (retval == 0) {
+    *pcluster = reinterpret_cast<rados_t>(new librados::RadosClient(cct));
+  }
+  tracepoint(librados, rados_create2_exit, retval, *pcluster);
+
+  cct->put();
+  return retval;
+}
+
+/* This function is intended for use by Ceph daemons. These daemons have
+ * already called global_init and want to use that particular configuration for
+ * their cluster.
+ */
+extern "C" int rados_create_with_context(rados_t *pcluster, rados_config_t cct_)
+{
+  CephContext *cct = (CephContext *)cct_;
+  TracepointProvider::initialize<tracepoint_traits>(cct);
+
+  tracepoint(librados, rados_create_with_context_enter, cct_);
+  librados::RadosClient *radosp = new librados::RadosClient(cct);
+  *pcluster = (void *)radosp;
+  tracepoint(librados, rados_create_with_context_exit, 0, *pcluster);
+  return 0;
+}
+
+extern "C" rados_config_t rados_cct(rados_t cluster)
+{
+  tracepoint(librados, rados_cct_enter, cluster);
+  librados::RadosClient *client = (librados::RadosClient *)cluster;
+  rados_config_t retval = (rados_config_t)client->cct;
+  tracepoint(librados, rados_cct_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_connect(rados_t cluster)
+{
+  tracepoint(librados, rados_connect_enter, cluster);
+  librados::RadosClient *client = (librados::RadosClient *)cluster;
+  int retval = client->connect();
+  tracepoint(librados, rados_connect_exit, retval);
+  return retval;
+}
+
+extern "C" void rados_shutdown(rados_t cluster)
+{
+  tracepoint(librados, rados_shutdown_enter, cluster);
+  librados::RadosClient *radosp = (librados::RadosClient *)cluster;
+  radosp->shutdown();
+  delete radosp;
+  tracepoint(librados, rados_shutdown_exit);
+}
+
+extern "C" uint64_t rados_get_instance_id(rados_t cluster)
+{
+  tracepoint(librados, rados_get_instance_id_enter, cluster);
+  librados::RadosClient *client = (librados::RadosClient *)cluster;
+  uint64_t retval = client->get_instance_id();
+  tracepoint(librados, rados_get_instance_id_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_get_min_compatible_osd(rados_t cluster,
+                                            int8_t* require_osd_release)
+{
+  librados::RadosClient *client = (librados::RadosClient *)cluster;
+  return client->get_min_compatible_osd(require_osd_release);
+}
+
+extern "C" int rados_get_min_compatible_client(rados_t cluster,
+                                               int8_t* min_compat_client,
+                                               int8_t* require_min_compat_client)
+{
+  librados::RadosClient *client = (librados::RadosClient *)cluster;
+  return client->get_min_compatible_client(min_compat_client,
+                                           require_min_compat_client);
+}
+
+extern "C" void rados_version(int *major, int *minor, int *extra)
+{
+  tracepoint(librados, rados_version_enter, major, minor, extra);
+  if (major)
+    *major = LIBRADOS_VER_MAJOR;
+  if (minor)
+    *minor = LIBRADOS_VER_MINOR;
+  if (extra)
+    *extra = LIBRADOS_VER_EXTRA;
+  tracepoint(librados, rados_version_exit, LIBRADOS_VER_MAJOR, LIBRADOS_VER_MINOR, LIBRADOS_VER_EXTRA);
+}
+
+
+// -- config --
+extern "C" int rados_conf_read_file(rados_t cluster, const char *path_list)
+{
+  tracepoint(librados, rados_conf_read_file_enter, cluster, path_list);
+  librados::RadosClient *client = (librados::RadosClient *)cluster;
+  auto& conf = client->cct->_conf;
+  ostringstream warnings;
+  int ret = conf.parse_config_files(path_list, &warnings, 0);
+  if (ret) {
+    if (warnings.tellp() > 0)
+      lderr(client->cct) << warnings.str() << dendl;
+    client->cct->_conf.complain_about_parse_errors(client->cct);
+    tracepoint(librados, rados_conf_read_file_exit, ret);
+    return ret;
+  }
+  conf.parse_env(); // environment variables override
+
+  conf.apply_changes(nullptr);
+  client->cct->_conf.complain_about_parse_errors(client->cct);
+  tracepoint(librados, rados_conf_read_file_exit, 0);
+  return 0;
+}
+
+extern "C" int rados_conf_parse_argv(rados_t cluster, int argc, const char **argv)
+{
+  tracepoint(librados, rados_conf_parse_argv_enter, cluster, argc);
+  int i;
+  for(i = 0; i < argc; i++) {
+    tracepoint(librados, rados_conf_parse_argv_arg, argv[i]);
+  }
+  librados::RadosClient *client = (librados::RadosClient *)cluster;
+  auto& conf = client->cct->_conf;
+  vector<const char*> args;
+  argv_to_vec(argc, argv, args);
+  int ret = conf.parse_argv(args);
+  if (ret) {
+    tracepoint(librados, rados_conf_parse_argv_exit, ret);
+    return ret;
+  }
+  conf.apply_changes(nullptr);
+  tracepoint(librados, rados_conf_parse_argv_exit, 0);
+  return 0;
+}
+
+// like above, but return the remainder of argv to contain remaining
+// unparsed args.  Must be allocated to at least argc by caller.
+// remargv will contain n <= argc pointers to original argv[], the end
+// of which may be NULL
+
+extern "C" int rados_conf_parse_argv_remainder(rados_t cluster, int argc,
+                                              const char **argv,
+                                              const char **remargv)
+{
+  tracepoint(librados, rados_conf_parse_argv_remainder_enter, cluster, argc);
+  unsigned int i;
+  for(i = 0; i < (unsigned int) argc; i++) {
+    tracepoint(librados, rados_conf_parse_argv_remainder_arg, argv[i]);
+  }
+  librados::RadosClient *client = (librados::RadosClient *)cluster;
+  auto& conf = client->cct->_conf;
+  vector<const char*> args;
+  for (int i=0; i<argc; i++)
+    args.push_back(argv[i]);
+  int ret = conf.parse_argv(args);
+  if (ret) {
+    tracepoint(librados, rados_conf_parse_argv_remainder_exit, ret);
+    return ret;
+  }
+  conf.apply_changes(NULL);
+  ceph_assert(args.size() <= (unsigned int)argc);
+  for (i = 0; i < (unsigned int)argc; ++i) {
+    if (i < args.size())
+      remargv[i] = args[i];
+    else
+      remargv[i] = (const char *)NULL;
+    tracepoint(librados, rados_conf_parse_argv_remainder_remarg, remargv[i]);
+  }
+  tracepoint(librados, rados_conf_parse_argv_remainder_exit, 0);
+  return 0;
+}
+
+extern "C" int rados_conf_parse_env(rados_t cluster, const char *env)
+{
+  tracepoint(librados, rados_conf_parse_env_enter, cluster, env);
+  librados::RadosClient *client = (librados::RadosClient *)cluster;
+  auto& conf = client->cct->_conf;
+  conf.parse_env(env);
+  conf.apply_changes(nullptr);
+  tracepoint(librados, rados_conf_parse_env_exit, 0);
+  return 0;
+}
+
+extern "C" int rados_conf_set(rados_t cluster, const char *option, const char *value)
+{
+  tracepoint(librados, rados_conf_set_enter, cluster, option, value);
+  librados::RadosClient *client = (librados::RadosClient *)cluster;
+  auto& conf = client->cct->_conf;
+  int ret = conf.set_val(option, value);
+  if (ret) {
+    tracepoint(librados, rados_conf_set_exit, ret);
+    return ret;
+  }
+  conf.apply_changes(nullptr);
+  tracepoint(librados, rados_conf_set_exit, 0);
+  return 0;
+}
+
+/* cluster info */
+extern "C" int rados_cluster_stat(rados_t cluster, rados_cluster_stat_t *result)
+{
+  tracepoint(librados, rados_cluster_stat_enter, cluster);
+  librados::RadosClient *client = (librados::RadosClient *)cluster;
+
+  ceph_statfs stats;
+  int r = client->get_fs_stats(stats);
+  result->kb = stats.kb;
+  result->kb_used = stats.kb_used;
+  result->kb_avail = stats.kb_avail;
+  result->num_objects = stats.num_objects;
+  tracepoint(librados, rados_cluster_stat_exit, r, result->kb, result->kb_used, result->kb_avail, result->num_objects);
+  return r;
+}
+
+extern "C" int rados_conf_get(rados_t cluster, const char *option, char *buf, size_t len)
+{
+  tracepoint(librados, rados_conf_get_enter, cluster, option, len);
+  char *tmp = buf;
+  librados::RadosClient *client = (librados::RadosClient *)cluster;
+  const auto& conf = client->cct->_conf;
+  int retval = conf.get_val(option, &tmp, len);
+  tracepoint(librados, rados_conf_get_exit, retval, retval ? "" : option);
+  return retval;
+}
+
+extern "C" int64_t rados_pool_lookup(rados_t cluster, const char *name)
+{
+  tracepoint(librados, rados_pool_lookup_enter, cluster, name);
+  librados::RadosClient *radosp = (librados::RadosClient *)cluster;
+  int64_t retval = radosp->lookup_pool(name);
+  tracepoint(librados, rados_pool_lookup_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_pool_reverse_lookup(rados_t cluster, int64_t id,
+                                        char *buf, size_t maxlen)
+{
+  tracepoint(librados, rados_pool_reverse_lookup_enter, cluster, id, maxlen);
+  librados::RadosClient *radosp = (librados::RadosClient *)cluster;
+  std::string name;
+  int r = radosp->pool_get_name(id, &name);
+  if (r < 0) {
+    tracepoint(librados, rados_pool_reverse_lookup_exit, r, "");
+    return r;
+  }
+  if (name.length() >= maxlen) {
+    tracepoint(librados, rados_pool_reverse_lookup_exit, -ERANGE, "");
+    return -ERANGE;
+  }
+  strcpy(buf, name.c_str());
+  int retval = name.length();
+  tracepoint(librados, rados_pool_reverse_lookup_exit, retval, buf);
+  return retval;
+}
+
+extern "C" int rados_cluster_fsid(rados_t cluster, char *buf,
+                                 size_t maxlen)
+{
+  tracepoint(librados, rados_cluster_fsid_enter, cluster, maxlen);
+  librados::RadosClient *radosp = (librados::RadosClient *)cluster;
+  std::string fsid;
+  radosp->get_fsid(&fsid);
+  if (fsid.length() >= maxlen) {
+    tracepoint(librados, rados_cluster_fsid_exit, -ERANGE, "");
+    return -ERANGE;
+  }
+  strcpy(buf, fsid.c_str());
+  int retval = fsid.length();
+  tracepoint(librados, rados_cluster_fsid_exit, retval, buf);
+  return retval;
+}
+
+extern "C" int rados_wait_for_latest_osdmap(rados_t cluster)
+{
+  tracepoint(librados, rados_wait_for_latest_osdmap_enter, cluster);
+  librados::RadosClient *radosp = (librados::RadosClient *)cluster;
+  int retval = radosp->wait_for_latest_osdmap();
+  tracepoint(librados, rados_wait_for_latest_osdmap_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_blacklist_add(rados_t cluster, char *client_address,
+                                  uint32_t expire_seconds)
+{
+  librados::RadosClient *radosp = (librados::RadosClient *)cluster;
+  return radosp->blacklist_add(client_address, expire_seconds);
+}
+
+extern "C" void rados_set_osdmap_full_try(rados_ioctx_t io)
+{
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  ctx->objecter->set_osdmap_full_try();
+}
+
+extern "C" void rados_unset_osdmap_full_try(rados_ioctx_t io)
+{
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  ctx->objecter->unset_osdmap_full_try();
+}
+
+extern "C" int rados_application_enable(rados_ioctx_t io, const char *app_name,
+                                        int force)
+{
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  return ctx->application_enable(app_name, force != 0);
+}
+
+extern "C" int rados_application_list(rados_ioctx_t io, char *values,
+                                      size_t *values_len)
+{
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  std::set<std::string> app_names;
+  int r = ctx->application_list(&app_names);
+  if (r < 0) {
+    return r;
+  }
+
+  size_t total_len = 0;
+  for (auto app_name : app_names) {
+    total_len += app_name.size() + 1;
+  }
+
+  if (*values_len < total_len) {
+    *values_len = total_len;
+    return -ERANGE;
+  }
+
+  char *values_p = values;
+  for (auto app_name : app_names) {
+    size_t len = app_name.size() + 1;
+    strncpy(values_p, app_name.c_str(), len);
+    values_p += len;
+  }
+  *values_p = '\0';
+  *values_len = total_len;
+  return 0;
+}
+
+extern "C" int rados_application_metadata_get(rados_ioctx_t io,
+                                              const char *app_name,
+                                              const char *key, char *value,
+                                              size_t *value_len)
+{
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  std::string value_str;
+  int r = ctx->application_metadata_get(app_name, key, &value_str);
+  if (r < 0) {
+    return r;
+  }
+
+  size_t len = value_str.size() + 1;
+  if (*value_len < len) {
+    *value_len = len;
+    return -ERANGE;
+  }
+
+  strncpy(value, value_str.c_str(), len);
+  *value_len = len;
+  return 0;
+}
+
+extern "C" int rados_application_metadata_set(rados_ioctx_t io,
+                                              const char *app_name,
+                                              const char *key,
+                                              const char *value)
+{
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  return ctx->application_metadata_set(app_name, key, value);
+}
+
+extern "C" int rados_application_metadata_remove(rados_ioctx_t io,
+                                                 const char *app_name,
+                                                 const char *key)
+{
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  return ctx->application_metadata_remove(app_name, key);
+}
+
+extern "C" int rados_application_metadata_list(rados_ioctx_t io,
+                                               const char *app_name,
+                                               char *keys, size_t *keys_len,
+                                               char *values, size_t *vals_len)
+{
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  std::map<std::string, std::string> metadata;
+  int r = ctx->application_metadata_list(app_name, &metadata);
+  if (r < 0) {
+    return r;
+  }
+
+  size_t total_key_len = 0;
+  size_t total_val_len = 0;
+  for (auto pair : metadata) {
+    total_key_len += pair.first.size() + 1;
+    total_val_len += pair.second.size() + 1;
+  }
+
+  if (*keys_len < total_key_len || *vals_len < total_val_len) {
+    *keys_len = total_key_len;
+    *vals_len = total_val_len;
+    return -ERANGE;
+  }
+
+  char *keys_p = keys;
+  char *vals_p = values;
+  for (auto pair : metadata) {
+    size_t key_len = pair.first.size() + 1;
+    strncpy(keys_p, pair.first.c_str(), key_len);
+    keys_p += key_len;
+
+    size_t val_len = pair.second.size() + 1;
+    strncpy(vals_p, pair.second.c_str(), val_len);
+    vals_p += val_len;
+  }
+  *keys_p = '\0';
+  *keys_len = total_key_len;
+
+  *vals_p = '\0';
+  *vals_len = total_val_len;
+  return 0;
+}
+
+extern "C" int rados_pool_list(rados_t cluster, char *buf, size_t len)
+{
+  tracepoint(librados, rados_pool_list_enter, cluster, len);
+  librados::RadosClient *client = (librados::RadosClient *)cluster;
+  std::list<std::pair<int64_t, std::string> > pools;
+  int r = client->pool_list(pools);
+  if (r < 0) {
+    tracepoint(librados, rados_pool_list_exit, r);
+    return r;
+  }
+
+  if (len > 0 && !buf) {
+    tracepoint(librados, rados_pool_list_exit, -EINVAL);
+    return -EINVAL;
+  }
+
+  char *b = buf;
+  if (b)
+    memset(b, 0, len);
+  int needed = 0;
+  std::list<std::pair<int64_t, std::string> >::const_iterator i = pools.begin();
+  std::list<std::pair<int64_t, std::string> >::const_iterator p_end =
+    pools.end();
+  for (; i != p_end; ++i) {
+    int rl = i->second.length() + 1;
+    if (len < (unsigned)rl)
+      break;
+    const char* pool = i->second.c_str();
+    tracepoint(librados, rados_pool_list_pool, pool);
+    if (b) {
+      strncat(b, pool, rl);
+      b += rl;
+    }
+    needed += rl;
+    len -= rl;
+  }
+  for (; i != p_end; ++i) {
+    int rl = i->second.length() + 1;
+    needed += rl;
+  }
+  int retval = needed + 1;
+  tracepoint(librados, rados_pool_list_exit, retval);
+  return retval;
+}
+
+CEPH_RADOS_API int rados_inconsistent_pg_list(rados_t cluster, int64_t pool_id,
+                                             char *buf, size_t len)
+{
+  tracepoint(librados, rados_inconsistent_pg_list_enter, cluster, pool_id, len);
+  librados::RadosClient *client = (librados::RadosClient *)cluster;
+  std::vector<std::string> pgs;
+  if (int r = client->get_inconsistent_pgs(pool_id, &pgs); r < 0) {
+    tracepoint(librados, rados_inconsistent_pg_list_exit, r);
+    return r;
+  }
+
+  if (len > 0 && !buf) {
+    tracepoint(librados, rados_inconsistent_pg_list_exit, -EINVAL);
+    return -EINVAL;
+  }
+
+  char *b = buf;
+  if (b)
+    memset(b, 0, len);
+  int needed = 0;
+  for (const auto& s : pgs) {
+    unsigned rl = s.length() + 1;
+    if (b && len >= rl) {
+      tracepoint(librados, rados_inconsistent_pg_list_pg, s.c_str());
+      strncat(b, s.c_str(), rl);
+      b += rl;
+      len -= rl;
+    }
+    needed += rl;
+  }
+  int retval = needed + 1;
+  tracepoint(librados, rados_inconsistent_pg_list_exit, retval);
+  return retval;
+}
+
+
+static void dict_to_map(const char *dict,
+                        std::map<std::string, std::string>* dict_map)
+{
+  while (*dict != '\0') {
+    const char* key = dict;
+    dict += strlen(key) + 1;
+    const char* value = dict;
+    dict += strlen(value) + 1;
+    (*dict_map)[key] = value;
+  }
+}
+
+CEPH_RADOS_API int rados_service_register(rados_t cluster, const char *service,
+                                          const char *daemon,
+                                          const char *metadata_dict)
+{
+  librados::RadosClient *client = (librados::RadosClient *)cluster;
+
+  std::map<std::string, std::string> metadata;
+  dict_to_map(metadata_dict, &metadata);
+
+  return client->service_daemon_register(service, daemon, metadata);
+}
+
+CEPH_RADOS_API int rados_service_update_status(rados_t cluster,
+                                               const char *status_dict)
+{
+  librados::RadosClient *client = (librados::RadosClient *)cluster;
+
+  std::map<std::string, std::string> status;
+  dict_to_map(status_dict, &status);
+
+  return client->service_daemon_update_status(std::move(status));
+}
+
+static void do_out_buffer(bufferlist& outbl, char **outbuf, size_t *outbuflen)
+{
+  if (outbuf) {
+    if (outbl.length() > 0) {
+      *outbuf = (char *)malloc(outbl.length());
+      memcpy(*outbuf, outbl.c_str(), outbl.length());
+    } else {
+      *outbuf = NULL;
+    }
+  }
+  if (outbuflen)
+    *outbuflen = outbl.length();
+}
+
+static void do_out_buffer(string& outbl, char **outbuf, size_t *outbuflen)
+{
+  if (outbuf) {
+    if (outbl.length() > 0) {
+      *outbuf = (char *)malloc(outbl.length());
+      memcpy(*outbuf, outbl.c_str(), outbl.length());
+    } else {
+      *outbuf = NULL;
+    }
+  }
+  if (outbuflen)
+    *outbuflen = outbl.length();
+}
+
+extern "C" int rados_ping_monitor(rados_t cluster, const char *mon_id,
+                                  char **outstr, size_t *outstrlen)
+{
+  tracepoint(librados, rados_ping_monitor_enter, cluster, mon_id);
+  librados::RadosClient *client = (librados::RadosClient *)cluster;
+  string str;
+
+  if (!mon_id) {
+    tracepoint(librados, rados_ping_monitor_exit, -EINVAL, NULL, NULL);
+    return -EINVAL;
+  }
+
+  int ret = client->ping_monitor(mon_id, &str);
+  if (ret == 0) {
+    do_out_buffer(str, outstr, outstrlen);
+  }
+  tracepoint(librados, rados_ping_monitor_exit, ret, ret < 0 ? NULL : outstr, ret < 0 ? NULL : outstrlen);
+  return ret;
+}
+
+extern "C" int rados_mon_command(rados_t cluster, const char **cmd,
+                                size_t cmdlen,
+                                const char *inbuf, size_t inbuflen,
+                                char **outbuf, size_t *outbuflen,
+                                char **outs, size_t *outslen)
+{
+  tracepoint(librados, rados_mon_command_enter, cluster, cmdlen, inbuf, inbuflen);
+  librados::RadosClient *client = (librados::RadosClient *)cluster;
+  bufferlist inbl;
+  bufferlist outbl;
+  string outstring;
+  vector<string> cmdvec;
+
+  for (size_t i = 0; i < cmdlen; i++) {
+    tracepoint(librados, rados_mon_command_cmd, cmd[i]);
+    cmdvec.push_back(cmd[i]);
+  }
+
+  inbl.append(inbuf, inbuflen);
+  int ret = client->mon_command(cmdvec, inbl, &outbl, &outstring);
+
+  do_out_buffer(outbl, outbuf, outbuflen);
+  do_out_buffer(outstring, outs, outslen);
+  tracepoint(librados, rados_mon_command_exit, ret, outbuf, outbuflen, outs, outslen);
+  return ret;
+}
+
+extern "C" int rados_mon_command_target(rados_t cluster, const char *name,
+                                       const char **cmd,
+                                       size_t cmdlen,
+                                       const char *inbuf, size_t inbuflen,
+                                       char **outbuf, size_t *outbuflen,
+                                       char **outs, size_t *outslen)
+{
+  tracepoint(librados, rados_mon_command_target_enter, cluster, name, cmdlen, inbuf, inbuflen);
+  librados::RadosClient *client = (librados::RadosClient *)cluster;
+  bufferlist inbl;
+  bufferlist outbl;
+  string outstring;
+  vector<string> cmdvec;
+
+  // is this a numeric id?
+  char *endptr;
+  errno = 0;
+  long rank = strtol(name, &endptr, 10);
+  if ((errno == ERANGE && (rank == LONG_MAX || rank == LONG_MIN)) ||
+      (errno != 0 && rank == 0) ||
+      endptr == name ||    // no digits
+      *endptr != '\0') {   // extra characters
+    rank = -1;
+  }
+
+  for (size_t i = 0; i < cmdlen; i++) {
+    tracepoint(librados, rados_mon_command_target_cmd, cmd[i]);
+    cmdvec.push_back(cmd[i]);
+  }
+
+  inbl.append(inbuf, inbuflen);
+  int ret;
+  if (rank >= 0)
+    ret = client->mon_command(rank, cmdvec, inbl, &outbl, &outstring);
+  else
+    ret = client->mon_command(name, cmdvec, inbl, &outbl, &outstring);
+
+  do_out_buffer(outbl, outbuf, outbuflen);
+  do_out_buffer(outstring, outs, outslen);
+  tracepoint(librados, rados_mon_command_target_exit, ret, outbuf, outbuflen, outs, outslen);
+  return ret;
+}
+
+extern "C" int rados_osd_command(rados_t cluster, int osdid, const char **cmd,
+                                size_t cmdlen,
+                                const char *inbuf, size_t inbuflen,
+                                char **outbuf, size_t *outbuflen,
+                                char **outs, size_t *outslen)
+{
+  tracepoint(librados, rados_osd_command_enter, cluster, osdid, cmdlen, inbuf, inbuflen);
+  librados::RadosClient *client = (librados::RadosClient *)cluster;
+  bufferlist inbl;
+  bufferlist outbl;
+  string outstring;
+  vector<string> cmdvec;
+
+  for (size_t i = 0; i < cmdlen; i++) {
+    tracepoint(librados, rados_osd_command_cmd, cmd[i]);
+    cmdvec.push_back(cmd[i]);
+  }
+
+  inbl.append(inbuf, inbuflen);
+  int ret = client->osd_command(osdid, cmdvec, inbl, &outbl, &outstring);
+
+  do_out_buffer(outbl, outbuf, outbuflen);
+  do_out_buffer(outstring, outs, outslen);
+  tracepoint(librados, rados_osd_command_exit, ret, outbuf, outbuflen, outs, outslen);
+  return ret;
+}
+
+extern "C" int rados_mgr_command(rados_t cluster, const char **cmd,
+                                size_t cmdlen,
+                                const char *inbuf, size_t inbuflen,
+                                char **outbuf, size_t *outbuflen,
+                                char **outs, size_t *outslen)
+{
+  tracepoint(librados, rados_mgr_command_enter, cluster, cmdlen, inbuf,
+      inbuflen);
+
+  librados::RadosClient *client = (librados::RadosClient *)cluster;
+  bufferlist inbl;
+  bufferlist outbl;
+  string outstring;
+  vector<string> cmdvec;
+
+  for (size_t i = 0; i < cmdlen; i++) {
+    tracepoint(librados, rados_mgr_command_cmd, cmd[i]);
+    cmdvec.push_back(cmd[i]);
+  }
+
+  inbl.append(inbuf, inbuflen);
+  int ret = client->mgr_command(cmdvec, inbl, &outbl, &outstring);
+
+  do_out_buffer(outbl, outbuf, outbuflen);
+  do_out_buffer(outstring, outs, outslen);
+  tracepoint(librados, rados_mgr_command_exit, ret, outbuf, outbuflen, outs,
+      outslen);
+  return ret;
+}
+
+extern "C" int rados_pg_command(rados_t cluster, const char *pgstr,
+                               const char **cmd, size_t cmdlen,
+                               const char *inbuf, size_t inbuflen,
+                               char **outbuf, size_t *outbuflen,
+                               char **outs, size_t *outslen)
+{
+  tracepoint(librados, rados_pg_command_enter, cluster, pgstr, cmdlen, inbuf, inbuflen);
+  librados::RadosClient *client = (librados::RadosClient *)cluster;
+  bufferlist inbl;
+  bufferlist outbl;
+  string outstring;
+  pg_t pgid;
+  vector<string> cmdvec;
+
+  for (size_t i = 0; i < cmdlen; i++) {
+    tracepoint(librados, rados_pg_command_cmd, cmd[i]);
+    cmdvec.push_back(cmd[i]);
+  }
+
+  inbl.append(inbuf, inbuflen);
+  if (!pgid.parse(pgstr))
+    return -EINVAL;
+
+  int ret = client->pg_command(pgid, cmdvec, inbl, &outbl, &outstring);
+
+  do_out_buffer(outbl, outbuf, outbuflen);
+  do_out_buffer(outstring, outs, outslen);
+  tracepoint(librados, rados_pg_command_exit, ret, outbuf, outbuflen, outs, outslen);
+  return ret;
+}
+
+extern "C" void rados_buffer_free(char *buf)
+{
+  tracepoint(librados, rados_buffer_free_enter, buf);
+  if (buf)
+    free(buf);
+  tracepoint(librados, rados_buffer_free_exit);
+}
+
+extern "C" int rados_monitor_log(rados_t cluster, const char *level, rados_log_callback_t cb, void *arg)
+{
+  tracepoint(librados, rados_monitor_log_enter, cluster, level, cb, arg);
+  librados::RadosClient *client = (librados::RadosClient *)cluster;
+  int retval = client->monitor_log(level, cb, nullptr, arg);
+  tracepoint(librados, rados_monitor_log_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_monitor_log2(rados_t cluster, const char *level,
+                                 rados_log_callback2_t cb, void *arg)
+{
+  tracepoint(librados, rados_monitor_log2_enter, cluster, level, cb, arg);
+  librados::RadosClient *client = (librados::RadosClient *)cluster;
+  int retval = client->monitor_log(level, nullptr, cb, arg);
+  tracepoint(librados, rados_monitor_log2_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_ioctx_create(rados_t cluster, const char *name, rados_ioctx_t *io)
+{
+  tracepoint(librados, rados_ioctx_create_enter, cluster, name);
+  librados::RadosClient *client = (librados::RadosClient *)cluster;
+  librados::IoCtxImpl *ctx;
+
+  int r = client->create_ioctx(name, &ctx);
+  if (r < 0) {
+    tracepoint(librados, rados_ioctx_create_exit, r, NULL);
+    return r;
+  }
+
+  *io = ctx;
+  ctx->get();
+  tracepoint(librados, rados_ioctx_create_exit, 0, ctx);
+  return 0;
+}
+
+extern "C" int rados_ioctx_create2(rados_t cluster, int64_t pool_id,
+                                   rados_ioctx_t *io)
+{
+  tracepoint(librados, rados_ioctx_create2_enter, cluster, pool_id);
+  librados::RadosClient *client = (librados::RadosClient *)cluster;
+  librados::IoCtxImpl *ctx;
+
+  int r = client->create_ioctx(pool_id, &ctx);
+  if (r < 0) {
+    tracepoint(librados, rados_ioctx_create2_exit, r, NULL);
+    return r;
+  }
+
+  *io = ctx;
+  ctx->get();
+  tracepoint(librados, rados_ioctx_create2_exit, 0, ctx);
+  return 0;
+}
+
+extern "C" void rados_ioctx_destroy(rados_ioctx_t io)
+{
+  tracepoint(librados, rados_ioctx_destroy_enter, io);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  ctx->put();
+  tracepoint(librados, rados_ioctx_destroy_exit);
+}
+
+extern "C" int rados_ioctx_pool_stat(rados_ioctx_t io, struct rados_pool_stat_t *stats)
+{
+  tracepoint(librados, rados_ioctx_pool_stat_enter, io);
+  librados::IoCtxImpl *io_ctx_impl = (librados::IoCtxImpl *)io;
+  list<string> ls;
+  std::string pool_name;
+
+  int err = io_ctx_impl->client->pool_get_name(io_ctx_impl->get_id(), &pool_name);
+  if (err) {
+    tracepoint(librados, rados_ioctx_pool_stat_exit, err, stats);
+    return err;
+  }
+  ls.push_back(pool_name);
+
+  map<string, ::pool_stat_t> rawresult;
+  err = io_ctx_impl->client->get_pool_stats(ls, rawresult);
+  if (err) {
+    tracepoint(librados, rados_ioctx_pool_stat_exit, err, stats);
+    return err;
+  }
+
+  ::pool_stat_t& r = rawresult[pool_name];
+  stats->num_kb = shift_round_up(r.stats.sum.num_bytes, 10);
+  stats->num_bytes = r.stats.sum.num_bytes;
+  stats->num_objects = r.stats.sum.num_objects;
+  stats->num_object_clones = r.stats.sum.num_object_clones;
+  stats->num_object_copies = r.stats.sum.num_object_copies;
+  stats->num_objects_missing_on_primary = r.stats.sum.num_objects_missing_on_primary;
+  stats->num_objects_unfound = r.stats.sum.num_objects_unfound;
+  stats->num_objects_degraded =
+    r.stats.sum.num_objects_degraded +
+    r.stats.sum.num_objects_misplaced; // FIXME: this is imprecise
+  stats->num_rd = r.stats.sum.num_rd;
+  stats->num_rd_kb = r.stats.sum.num_rd_kb;
+  stats->num_wr = r.stats.sum.num_wr;
+  stats->num_wr_kb = r.stats.sum.num_wr_kb;
+  tracepoint(librados, rados_ioctx_pool_stat_exit, 0, stats);
+  return 0;
+}
+
+extern "C" rados_config_t rados_ioctx_cct(rados_ioctx_t io)
+{
+  tracepoint(librados, rados_ioctx_cct_enter, io);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  rados_config_t retval = (rados_config_t)ctx->client->cct;
+  tracepoint(librados, rados_ioctx_cct_exit, retval);
+  return retval;
+}
+
+extern "C" void rados_ioctx_snap_set_read(rados_ioctx_t io, rados_snap_t seq)
+{
+  tracepoint(librados, rados_ioctx_snap_set_read_enter, io, seq);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  ctx->set_snap_read((snapid_t)seq);
+  tracepoint(librados, rados_ioctx_snap_set_read_exit);
+}
+
+extern "C" int rados_ioctx_selfmanaged_snap_set_write_ctx(rados_ioctx_t io,
+           rados_snap_t seq, rados_snap_t *snaps, int num_snaps)
+{
+  tracepoint(librados, rados_ioctx_selfmanaged_snap_set_write_ctx_enter, io, seq, snaps, num_snaps);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  vector<snapid_t> snv;
+  snv.resize(num_snaps);
+  for (int i=0; i<num_snaps; i++) {
+    snv[i] = (snapid_t)snaps[i];
+  }
+  int retval = ctx->set_snap_write_context((snapid_t)seq, snv);
+  tracepoint(librados, rados_ioctx_selfmanaged_snap_set_write_ctx_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_write(rados_ioctx_t io, const char *o, const char *buf, size_t len, uint64_t off)
+{
+  tracepoint(librados, rados_write_enter, io, o, buf, len, off);
+  if (len > UINT_MAX/2)
+    return -E2BIG;
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  object_t oid(o);
+  bufferlist bl;
+  bl.append(buf, len);
+  int retval = ctx->write(oid, bl, len, off);
+  tracepoint(librados, rados_write_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_append(rados_ioctx_t io, const char *o, const char *buf, size_t len)
+{
+  tracepoint(librados, rados_append_enter, io, o, buf, len);
+  if (len > UINT_MAX/2)
+    return -E2BIG;
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  object_t oid(o);
+  bufferlist bl;
+  bl.append(buf, len);
+  int retval = ctx->append(oid, bl, len);
+  tracepoint(librados, rados_append_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_write_full(rados_ioctx_t io, const char *o, const char *buf, size_t len)
+{
+  tracepoint(librados, rados_write_full_enter, io, o, buf, len);
+  if (len > UINT_MAX/2)
+    return -E2BIG;
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  object_t oid(o);
+  bufferlist bl;
+  bl.append(buf, len);
+  int retval = ctx->write_full(oid, bl);
+  tracepoint(librados, rados_write_full_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_writesame(rados_ioctx_t io,
+                               const char *o,
+                               const char *buf,
+                               size_t data_len,
+                               size_t write_len,
+                               uint64_t off)
+{
+  tracepoint(librados, rados_writesame_enter, io, o, buf, data_len, write_len, off);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  object_t oid(o);
+  bufferlist bl;
+  bl.append(buf, data_len);
+  int retval = ctx->writesame(oid, bl, write_len, off);
+  tracepoint(librados, rados_writesame_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_trunc(rados_ioctx_t io, const char *o, uint64_t size)
+{
+  tracepoint(librados, rados_trunc_enter, io, o, size);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  object_t oid(o);
+  int retval = ctx->trunc(oid, size);
+  tracepoint(librados, rados_trunc_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_remove(rados_ioctx_t io, const char *o)
+{
+  tracepoint(librados, rados_remove_enter, io, o);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  object_t oid(o);
+  int retval = ctx->remove(oid);
+  tracepoint(librados, rados_remove_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_read(rados_ioctx_t io, const char *o, char *buf, size_t len, uint64_t off)
+{
+  tracepoint(librados, rados_read_enter, io, o, buf, len, off);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  int ret;
+  object_t oid(o);
+
+  bufferlist bl;
+  bufferptr bp = buffer::create_static(len, buf);
+  bl.push_back(bp);
+
+  ret = ctx->read(oid, bl, len, off);
+  if (ret >= 0) {
+    if (bl.length() > len) {
+      tracepoint(librados, rados_read_exit, -ERANGE, NULL);
+      return -ERANGE;
+    }
+    if (!bl.is_provided_buffer(buf))
+      bl.copy(0, bl.length(), buf);
+    ret = bl.length();    // hrm :/
+  }
+
+  tracepoint(librados, rados_read_exit, ret, buf);
+  return ret;
+}
+
+extern "C" int rados_checksum(rados_ioctx_t io, const char *o,
+                              rados_checksum_type_t type,
+                              const char *init_value, size_t init_value_len,
+                              size_t len, uint64_t off, size_t chunk_size,
+                             char *pchecksum, size_t checksum_len)
+{
+  tracepoint(librados, rados_checksum_enter, io, o, type, init_value,
+            init_value_len, len, off, chunk_size);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  object_t oid(o);
+
+  bufferlist init_value_bl;
+  init_value_bl.append(init_value, init_value_len);
+
+  bufferlist checksum_bl;
+
+  int retval = ctx->checksum(oid, get_checksum_op_type(type), init_value_bl,
+                            len, off, chunk_size, &checksum_bl);
+  if (retval >= 0) {
+    if (checksum_bl.length() > checksum_len) {
+      tracepoint(librados, rados_checksum_exit, -ERANGE, NULL, 0);
+      return -ERANGE;
+    }
+
+    checksum_bl.copy(0, checksum_bl.length(), pchecksum);
+  }
+  tracepoint(librados, rados_checksum_exit, retval, pchecksum, checksum_len);
+  return retval;
+}
+
+extern "C" uint64_t rados_get_last_version(rados_ioctx_t io)
+{
+  tracepoint(librados, rados_get_last_version_enter, io);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  uint64_t retval = ctx->last_version();
+  tracepoint(librados, rados_get_last_version_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_pool_create(rados_t cluster, const char *name)
+{
+  tracepoint(librados, rados_pool_create_enter, cluster, name);
+  librados::RadosClient *radosp = (librados::RadosClient *)cluster;
+  string sname(name);
+  int retval = radosp->pool_create(sname);
+  tracepoint(librados, rados_pool_create_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_pool_create_with_auid(rados_t cluster, const char *name,
+                                          uint64_t auid)
+{
+  tracepoint(librados, rados_pool_create_with_auid_enter, cluster, name, auid);
+  librados::RadosClient *radosp = (librados::RadosClient *)cluster;
+  string sname(name);
+  int retval = 0;
+  if (auid != CEPH_AUTH_UID_DEFAULT) {
+    retval = -EINVAL;
+  } else {
+    retval = radosp->pool_create(sname);
+  }
+  tracepoint(librados, rados_pool_create_with_auid_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_pool_create_with_crush_rule(rados_t cluster, const char *name,
+                                                __u8 crush_rule_num)
+{
+  tracepoint(librados, rados_pool_create_with_crush_rule_enter, cluster, name, crush_rule_num);
+  librados::RadosClient *radosp = (librados::RadosClient *)cluster;
+  string sname(name);
+  int retval = radosp->pool_create(sname, crush_rule_num);
+  tracepoint(librados, rados_pool_create_with_crush_rule_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_pool_create_with_all(rados_t cluster, const char *name,
+                                         uint64_t auid, __u8 crush_rule_num)
+{
+  tracepoint(librados, rados_pool_create_with_all_enter, cluster, name, auid, crush_rule_num);
+  librados::RadosClient *radosp = (librados::RadosClient *)cluster;
+  string sname(name);
+  int retval = 0;
+  if (auid != CEPH_AUTH_UID_DEFAULT) {
+    retval = -EINVAL;
+  } else {
+    retval = radosp->pool_create(sname, crush_rule_num);
+  }
+  tracepoint(librados, rados_pool_create_with_all_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_pool_get_base_tier(rados_t cluster, int64_t pool_id, int64_t* base_tier)
+{
+  tracepoint(librados, rados_pool_get_base_tier_enter, cluster, pool_id);
+  librados::RadosClient *client = (librados::RadosClient *)cluster;
+  int retval = client->pool_get_base_tier(pool_id, base_tier);
+  tracepoint(librados, rados_pool_get_base_tier_exit, retval, *base_tier);
+  return retval;
+}
+
+extern "C" int rados_pool_delete(rados_t cluster, const char *pool_name)
+{
+  tracepoint(librados, rados_pool_delete_enter, cluster, pool_name);
+  librados::RadosClient *client = (librados::RadosClient *)cluster;
+  int retval = client->pool_delete(pool_name);
+  tracepoint(librados, rados_pool_delete_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_ioctx_pool_set_auid(rados_ioctx_t io, uint64_t auid)
+{
+  tracepoint(librados, rados_ioctx_pool_set_auid_enter, io, auid);
+  int retval = -EOPNOTSUPP;
+  tracepoint(librados, rados_ioctx_pool_set_auid_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_ioctx_pool_get_auid(rados_ioctx_t io, uint64_t *auid)
+{
+  tracepoint(librados, rados_ioctx_pool_get_auid_enter, io);
+  int retval = -EOPNOTSUPP;
+  tracepoint(librados, rados_ioctx_pool_get_auid_exit, retval, *auid);
+  return retval;
+}
+
+extern "C" int rados_ioctx_pool_requires_alignment(rados_ioctx_t io)
+{
+  tracepoint(librados, rados_ioctx_pool_requires_alignment_enter, io);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  int retval = ctx->client->pool_requires_alignment(ctx->get_id());
+  tracepoint(librados, rados_ioctx_pool_requires_alignment_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_ioctx_pool_requires_alignment2(rados_ioctx_t io,
+       int *requires)
+{
+  tracepoint(librados, rados_ioctx_pool_requires_alignment_enter2, io);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  bool requires_alignment;
+  int retval = ctx->client->pool_requires_alignment2(ctx->get_id(), 
+       &requires_alignment);
+  tracepoint(librados, rados_ioctx_pool_requires_alignment_exit2, retval, 
+       requires_alignment);
+  if (requires)
+    *requires = requires_alignment;
+  return retval;
+}
+
+extern "C" uint64_t rados_ioctx_pool_required_alignment(rados_ioctx_t io)
+{
+  tracepoint(librados, rados_ioctx_pool_required_alignment_enter, io);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  uint64_t retval = ctx->client->pool_required_alignment(ctx->get_id());
+  tracepoint(librados, rados_ioctx_pool_required_alignment_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_ioctx_pool_required_alignment2(rados_ioctx_t io,
+       uint64_t *alignment)
+{
+  tracepoint(librados, rados_ioctx_pool_required_alignment_enter2, io);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  int retval = ctx->client->pool_required_alignment2(ctx->get_id(),
+       alignment);
+  tracepoint(librados, rados_ioctx_pool_required_alignment_exit2, retval, 
+       *alignment);
+  return retval;
+}
+
+extern "C" void rados_ioctx_locator_set_key(rados_ioctx_t io, const char *key)
+{
+  tracepoint(librados, rados_ioctx_locator_set_key_enter, io, key);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  if (key)
+    ctx->oloc.key = key;
+  else
+    ctx->oloc.key = "";
+  tracepoint(librados, rados_ioctx_locator_set_key_exit);
+}
+
+extern "C" void rados_ioctx_set_namespace(rados_ioctx_t io, const char *nspace)
+{
+  tracepoint(librados, rados_ioctx_set_namespace_enter, io, nspace);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  if (nspace)
+    ctx->oloc.nspace = nspace;
+  else
+    ctx->oloc.nspace = "";
+  tracepoint(librados, rados_ioctx_set_namespace_exit);
+}
+
+extern "C" int rados_ioctx_get_namespace(rados_ioctx_t io, char *s,
+                                         unsigned maxlen)
+{
+  tracepoint(librados, rados_ioctx_get_namespace_enter, io, maxlen);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  auto length = ctx->oloc.nspace.length();
+  if (length >= maxlen) {
+    tracepoint(librados, rados_ioctx_get_namespace_exit, -ERANGE, "");
+    return -ERANGE;
+  }
+  strcpy(s, ctx->oloc.nspace.c_str());
+  int retval = (int)length;
+  tracepoint(librados, rados_ioctx_get_namespace_exit, retval, s);
+  return retval;
+}
+
+extern "C" rados_t rados_ioctx_get_cluster(rados_ioctx_t io)
+{
+  tracepoint(librados, rados_ioctx_get_cluster_enter, io);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  rados_t retval = (rados_t)ctx->client;
+  tracepoint(librados, rados_ioctx_get_cluster_exit, retval);
+  return retval;
+}
+
+extern "C" int64_t rados_ioctx_get_id(rados_ioctx_t io)
+{
+  tracepoint(librados, rados_ioctx_get_id_enter, io);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  int64_t retval = ctx->get_id();
+  tracepoint(librados, rados_ioctx_get_id_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_ioctx_get_pool_name(rados_ioctx_t io, char *s, unsigned maxlen)
+{
+  tracepoint(librados, rados_ioctx_get_pool_name_enter, io, maxlen);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  std::string pool_name;
+
+  int err = ctx->client->pool_get_name(ctx->get_id(), &pool_name);
+  if (err) {
+    tracepoint(librados, rados_ioctx_get_pool_name_exit, err, "");
+    return err;
+  }
+  if (pool_name.length() >= maxlen) {
+    tracepoint(librados, rados_ioctx_get_pool_name_exit, -ERANGE, "");
+    return -ERANGE;
+  }
+  strcpy(s, pool_name.c_str());
+  int retval = pool_name.length();
+  tracepoint(librados, rados_ioctx_get_pool_name_exit, retval, s);
+  return retval;
+}
+
+// snaps
+
+extern "C" int rados_ioctx_snap_create(rados_ioctx_t io, const char *snapname)
+{
+  tracepoint(librados, rados_ioctx_snap_create_enter, io, snapname);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  int retval = ctx->snap_create(snapname);
+  tracepoint(librados, rados_ioctx_snap_create_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_ioctx_snap_remove(rados_ioctx_t io, const char *snapname)
+{
+  tracepoint(librados, rados_ioctx_snap_remove_enter, io, snapname);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  int retval = ctx->snap_remove(snapname);
+  tracepoint(librados, rados_ioctx_snap_remove_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_ioctx_snap_rollback(rados_ioctx_t io, const char *oid,
+                             const char *snapname)
+{
+  tracepoint(librados, rados_ioctx_snap_rollback_enter, io, oid, snapname);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  int retval = ctx->rollback(oid, snapname);
+  tracepoint(librados, rados_ioctx_snap_rollback_exit, retval);
+  return retval;
+}
+
+// Deprecated name kept for backward compatibility
+extern "C" int rados_rollback(rados_ioctx_t io, const char *oid,
+                             const char *snapname)
+{
+  return rados_ioctx_snap_rollback(io, oid, snapname);
+}
+
+extern "C" int rados_ioctx_selfmanaged_snap_create(rados_ioctx_t io,
+                                            uint64_t *snapid)
+{
+  tracepoint(librados, rados_ioctx_selfmanaged_snap_create_enter, io);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  int retval = ctx->selfmanaged_snap_create(snapid);
+  tracepoint(librados, rados_ioctx_selfmanaged_snap_create_exit, retval, *snapid);
+  return retval;
+}
+
+extern "C" void
+rados_aio_ioctx_selfmanaged_snap_create(rados_ioctx_t io,
+                                        rados_snap_t *snapid,
+                                        rados_completion_t completion)
+{
+  tracepoint(librados, rados_ioctx_selfmanaged_snap_create_enter, io);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  librados::AioCompletionImpl *c = (librados::AioCompletionImpl*)completion;
+  ctx->aio_selfmanaged_snap_create(snapid, c);
+  tracepoint(librados, rados_ioctx_selfmanaged_snap_create_exit, 0, 0);
+}
+
+extern "C" int rados_ioctx_selfmanaged_snap_remove(rados_ioctx_t io,
+                                            uint64_t snapid)
+{
+  tracepoint(librados, rados_ioctx_selfmanaged_snap_remove_enter, io, snapid);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  int retval = ctx->selfmanaged_snap_remove(snapid);
+  tracepoint(librados, rados_ioctx_selfmanaged_snap_remove_exit, retval);
+  return retval;
+}
+
+extern "C" void
+rados_aio_ioctx_selfmanaged_snap_remove(rados_ioctx_t io,
+                                        rados_snap_t snapid,
+                                        rados_completion_t completion)
+{
+  tracepoint(librados, rados_ioctx_selfmanaged_snap_remove_enter, io, snapid);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  librados::AioCompletionImpl *c = (librados::AioCompletionImpl*)completion;
+  ctx->aio_selfmanaged_snap_remove(snapid, c);
+  tracepoint(librados, rados_ioctx_selfmanaged_snap_remove_exit, 0);
+}
+
+extern "C" int rados_ioctx_selfmanaged_snap_rollback(rados_ioctx_t io,
+                                                    const char *oid,
+                                                    uint64_t snapid)
+{
+  tracepoint(librados, rados_ioctx_selfmanaged_snap_rollback_enter, io, oid, snapid);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  int retval = ctx->selfmanaged_snap_rollback_object(oid, ctx->snapc, snapid);
+  tracepoint(librados, rados_ioctx_selfmanaged_snap_rollback_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_ioctx_snap_list(rados_ioctx_t io, rados_snap_t *snaps,
+                                   int maxlen)
+{
+  tracepoint(librados, rados_ioctx_snap_list_enter, io, maxlen);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  vector<uint64_t> snapvec;
+  int r = ctx->snap_list(&snapvec);
+  if (r < 0) {
+    tracepoint(librados, rados_ioctx_snap_list_exit, r, snaps, 0);
+    return r;
+  }
+  if ((int)snapvec.size() <= maxlen) {
+    for (unsigned i=0; i<snapvec.size(); i++) {
+      snaps[i] = snapvec[i];
+    }
+    int retval = snapvec.size();
+    tracepoint(librados, rados_ioctx_snap_list_exit, retval, snaps, retval);
+    return retval;
+  }
+  int retval = -ERANGE;
+  tracepoint(librados, rados_ioctx_snap_list_exit, retval, snaps, 0);
+  return retval;
+}
+
+extern "C" int rados_ioctx_snap_lookup(rados_ioctx_t io, const char *name,
+                                     rados_snap_t *id)
+{
+  tracepoint(librados, rados_ioctx_snap_lookup_enter, io, name);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  int retval = ctx->snap_lookup(name, (uint64_t *)id);
+  tracepoint(librados, rados_ioctx_snap_lookup_exit, retval, *id);
+  return retval;
+}
+
+extern "C" int rados_ioctx_snap_get_name(rados_ioctx_t io, rados_snap_t id,
+                                       char *name, int maxlen)
+{
+  tracepoint(librados, rados_ioctx_snap_get_name_enter, io, id, maxlen);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  std::string sname;
+  int r = ctx->snap_get_name(id, &sname);
+  if (r < 0) {
+    tracepoint(librados, rados_ioctx_snap_get_name_exit, r, "");
+    return r;
+  }
+  if ((int)sname.length() >= maxlen) {
+    int retval = -ERANGE;
+    tracepoint(librados, rados_ioctx_snap_get_name_exit, retval, "");
+    return retval;
+  }
+  strncpy(name, sname.c_str(), maxlen);
+  tracepoint(librados, rados_ioctx_snap_get_name_exit, 0, name);
+  return 0;
+}
+
+extern "C" int rados_ioctx_snap_get_stamp(rados_ioctx_t io, rados_snap_t id, time_t *t)
+{
+  tracepoint(librados, rados_ioctx_snap_get_stamp_enter, io, id);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  int retval = ctx->snap_get_stamp(id, t);
+  tracepoint(librados, rados_ioctx_snap_get_stamp_exit, retval, *t);
+  return retval;
+}
+
+extern "C" int rados_cmpext(rados_ioctx_t io, const char *o,
+                           const char *cmp_buf, size_t cmp_len, uint64_t off)
+{
+  tracepoint(librados, rados_cmpext_enter, io, o, cmp_buf, cmp_len, off);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  int ret;
+  object_t oid(o);
+
+  bufferlist cmp_bl;
+  cmp_bl.append(cmp_buf, cmp_len);
+
+  ret = ctx->cmpext(oid, off, cmp_bl);
+  tracepoint(librados, rados_cmpext_exit, ret);
+
+  return ret;
+}
+
+extern "C" int rados_getxattr(rados_ioctx_t io, const char *o, const char *name,
+                             char *buf, size_t len)
+{
+  tracepoint(librados, rados_getxattr_enter, io, o, name, len);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  int ret;
+  object_t oid(o);
+  bufferlist bl;
+  bl.push_back(buffer::create_static(len, buf));
+  ret = ctx->getxattr(oid, name, bl);
+  if (ret >= 0) {
+    if (bl.length() > len) {
+      tracepoint(librados, rados_getxattr_exit, -ERANGE, buf, 0);
+      return -ERANGE;
+    }
+    if (!bl.is_provided_buffer(buf))
+      bl.copy(0, bl.length(), buf);
+    ret = bl.length();
+  }
+
+  tracepoint(librados, rados_getxattr_exit, ret, buf, ret);
+  return ret;
+}
+
+extern "C" int rados_getxattrs(rados_ioctx_t io, const char *oid,
+                              rados_xattrs_iter_t *iter)
+{
+  tracepoint(librados, rados_getxattrs_enter, io, oid);
+  librados::RadosXattrsIter *it = new librados::RadosXattrsIter();
+  if (!it) {
+    tracepoint(librados, rados_getxattrs_exit, -ENOMEM, NULL);
+    return -ENOMEM;
+  }
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  object_t obj(oid);
+  int ret = ctx->getxattrs(obj, it->attrset);
+  if (ret) {
+    delete it;
+    tracepoint(librados, rados_getxattrs_exit, ret, NULL);
+    return ret;
+  }
+  it->i = it->attrset.begin();
+
+  *iter = it;
+  tracepoint(librados, rados_getxattrs_exit, 0, *iter);
+  return 0;
+}
+
+extern "C" int rados_getxattrs_next(rados_xattrs_iter_t iter,
+                                   const char **name, const char **val, size_t *len)
+{
+  tracepoint(librados, rados_getxattrs_next_enter, iter);
+  librados::RadosXattrsIter *it = static_cast<librados::RadosXattrsIter*>(iter);
+  if (it->val) {
+    free(it->val);
+    it->val = NULL;
+  }
+  if (it->i == it->attrset.end()) {
+    *name = NULL;
+    *val = NULL;
+    *len = 0;
+    tracepoint(librados, rados_getxattrs_next_exit, 0, NULL, NULL, 0);
+    return 0;
+  }
+  const std::string &s(it->i->first);
+  *name = s.c_str();
+  bufferlist &bl(it->i->second);
+  size_t bl_len = bl.length();
+  if (!bl_len) {
+    // malloc(0) is not guaranteed to return a valid pointer
+    *val = (char *)NULL;
+  } else {
+    it->val = (char*)malloc(bl_len);
+    if (!it->val) {
+      tracepoint(librados, rados_getxattrs_next_exit, -ENOMEM, *name, NULL, 0);
+      return -ENOMEM;
+    }
+    memcpy(it->val, bl.c_str(), bl_len);
+    *val = it->val;
+  }
+  *len = bl_len;
+  ++it->i;
+  tracepoint(librados, rados_getxattrs_next_exit, 0, *name, *val, *len);
+  return 0;
+}
+
+extern "C" void rados_getxattrs_end(rados_xattrs_iter_t iter)
+{
+  tracepoint(librados, rados_getxattrs_end_enter, iter);
+  librados::RadosXattrsIter *it = static_cast<librados::RadosXattrsIter*>(iter);
+  delete it;
+  tracepoint(librados, rados_getxattrs_end_exit);
+}
+
+extern "C" int rados_setxattr(rados_ioctx_t io, const char *o, const char *name, const char *buf, size_t len)
+{
+  tracepoint(librados, rados_setxattr_enter, io, o, name, buf, len);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  object_t oid(o);
+  bufferlist bl;
+  bl.append(buf, len);
+  int retval = ctx->setxattr(oid, name, bl);
+  tracepoint(librados, rados_setxattr_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_rmxattr(rados_ioctx_t io, const char *o, const char *name)
+{
+  tracepoint(librados, rados_rmxattr_enter, io, o, name);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  object_t oid(o);
+  int retval = ctx->rmxattr(oid, name);
+  tracepoint(librados, rados_rmxattr_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_stat(rados_ioctx_t io, const char *o, uint64_t *psize, time_t *pmtime)
+{
+  tracepoint(librados, rados_stat_enter, io, o);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  object_t oid(o);
+  int retval = ctx->stat(oid, psize, pmtime);
+  tracepoint(librados, rados_stat_exit, retval, psize, pmtime);
+  return retval;
+}
+
+extern "C" int rados_tmap_update(rados_ioctx_t io, const char *o, const char *cmdbuf, size_t cmdbuflen)
+{
+  tracepoint(librados, rados_tmap_update_enter, io, o, cmdbuf, cmdbuflen);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  object_t oid(o);
+  bufferlist cmdbl;
+  cmdbl.append(cmdbuf, cmdbuflen);
+  int retval = ctx->tmap_update(oid, cmdbl);
+  tracepoint(librados, rados_tmap_update_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_tmap_put(rados_ioctx_t io, const char *o, const char *buf, size_t buflen)
+{
+  tracepoint(librados, rados_tmap_put_enter, io, o, buf, buflen);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  object_t oid(o);
+  bufferlist bl;
+  bl.append(buf, buflen);
+  int retval = ctx->tmap_put(oid, bl);
+  tracepoint(librados, rados_tmap_put_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_tmap_get(rados_ioctx_t io, const char *o, char *buf, size_t buflen)
+{
+  tracepoint(librados, rados_tmap_get_enter, io, o, buflen);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  object_t oid(o);
+  bufferlist bl;
+  int r = ctx->tmap_get(oid, bl);
+  if (r < 0) {
+    tracepoint(librados, rados_tmap_get_exit, r, buf, 0);
+    return r;
+  }
+  if (bl.length() > buflen) {
+    tracepoint(librados, rados_tmap_get_exit, -ERANGE, buf, 0);
+    return -ERANGE;
+  }
+  bl.copy(0, bl.length(), buf);
+  int retval = bl.length();
+  tracepoint(librados, rados_tmap_get_exit, retval, buf, retval);
+  return retval;
+}
+
+extern "C" int rados_tmap_to_omap(rados_ioctx_t io, const char *o, bool nullok)
+{
+  tracepoint(librados, rados_tmap_to_omap_enter, io, o, nullok);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  object_t oid(o);
+  int retval = ctx->tmap_to_omap(oid, nullok);
+  tracepoint(librados, rados_tmap_to_omap_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_exec(rados_ioctx_t io, const char *o, const char *cls, const char *method,
+                         const char *inbuf, size_t in_len, char *buf, size_t out_len)
+{
+  tracepoint(librados, rados_exec_enter, io, o, cls, method, inbuf, in_len, out_len);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  object_t oid(o);
+  bufferlist inbl, outbl;
+  int ret;
+  inbl.append(inbuf, in_len);
+  ret = ctx->exec(oid, cls, method, inbl, outbl);
+  if (ret >= 0) {
+    if (outbl.length()) {
+      if (outbl.length() > out_len) {
+       tracepoint(librados, rados_exec_exit, -ERANGE, buf, 0);
+       return -ERANGE;
+      }
+      outbl.copy(0, outbl.length(), buf);
+      ret = outbl.length();   // hrm :/
+    }
+  }
+  tracepoint(librados, rados_exec_exit, ret, buf, ret);
+  return ret;
+}
+
+extern "C" rados_object_list_cursor rados_object_list_begin(rados_ioctx_t io)
+{
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+
+  hobject_t *result = new hobject_t(ctx->objecter->enumerate_objects_begin());
+  return (rados_object_list_cursor)result;
+}
+
+extern "C" rados_object_list_cursor rados_object_list_end(rados_ioctx_t io)
+{
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+
+  hobject_t *result = new hobject_t(ctx->objecter->enumerate_objects_end());
+  return (rados_object_list_cursor)result;
+}
+
+extern "C" int rados_object_list_is_end(
+    rados_ioctx_t io, rados_object_list_cursor cur)
+{
+  hobject_t *hobj = (hobject_t*)cur;
+  return hobj->is_max();
+}
+
+extern "C" void rados_object_list_cursor_free(
+    rados_ioctx_t io, rados_object_list_cursor cur)
+{
+  hobject_t *hobj = (hobject_t*)cur;
+  delete hobj;
+}
+
+extern "C" int rados_object_list_cursor_cmp(
+    rados_ioctx_t io,
+    rados_object_list_cursor lhs_cur,
+    rados_object_list_cursor rhs_cur)
+{
+  hobject_t *lhs = (hobject_t*)lhs_cur;
+  hobject_t *rhs = (hobject_t*)rhs_cur;
+  return cmp(*lhs, *rhs);
+}
+
+extern "C" int rados_object_list(rados_ioctx_t io,
+    const rados_object_list_cursor start,
+    const rados_object_list_cursor finish,
+    const size_t result_item_count,
+    const char *filter_buf,
+    const size_t filter_buf_len,
+    rados_object_list_item *result_items,
+    rados_object_list_cursor *next)
+{
+  ceph_assert(next);
+
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+
+  // Zero out items so that they will be safe to free later
+  memset(result_items, 0, sizeof(rados_object_list_item) * result_item_count);
+
+  std::list<librados::ListObjectImpl> result;
+  hobject_t next_hash;
+
+  bufferlist filter_bl;
+  if (filter_buf != nullptr) {
+    filter_bl.append(filter_buf, filter_buf_len);
+  }
+
+  C_SaferCond cond;
+  ctx->objecter->enumerate_objects(
+      ctx->poolid,
+      ctx->oloc.nspace,
+      *((hobject_t*)start),
+      *((hobject_t*)finish),
+      result_item_count,
+      filter_bl,
+      &result,
+      &next_hash,
+      &cond);
+
+  hobject_t *next_hobj = (hobject_t*)(*next);
+  ceph_assert(next_hobj);
+
+  int r = cond.wait();
+  if (r < 0) {
+    *next_hobj = hobject_t::get_max();
+    return r;
+  }
+
+  ceph_assert(result.size() <= result_item_count);  // Don't overflow!
+
+  int k = 0;
+  for (std::list<librados::ListObjectImpl>::iterator i = result.begin();
+       i != result.end(); ++i) {
+    rados_object_list_item &item = result_items[k++];
+    do_out_buffer(i->oid, &item.oid, &item.oid_length);
+    do_out_buffer(i->nspace, &item.nspace, &item.nspace_length);
+    do_out_buffer(i->locator, &item.locator, &item.locator_length);
+  }
+
+  *next_hobj = next_hash;
+
+  return result.size();
+}
+
+extern "C" void rados_object_list_free(
+    const size_t result_size,
+    rados_object_list_item *results)
+{
+  ceph_assert(results);
+
+  for (unsigned int i = 0; i < result_size; ++i) {
+    rados_buffer_free(results[i].oid);
+    rados_buffer_free(results[i].locator);
+    rados_buffer_free(results[i].nspace);
+  }
+}
+
+/* list objects */
+
+extern "C" int rados_nobjects_list_open(rados_ioctx_t io, rados_list_ctx_t *listh)
+{
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+
+  tracepoint(librados, rados_nobjects_list_open_enter, io);
+
+  Objecter::NListContext *h = new Objecter::NListContext;
+  h->pool_id = ctx->poolid;
+  h->pool_snap_seq = ctx->snap_seq;
+  h->nspace = ctx->oloc.nspace;        // After dropping compatibility need nspace
+  *listh = (void *)new librados::ObjListCtx(ctx, h);
+  tracepoint(librados, rados_nobjects_list_open_exit, 0, *listh);
+  return 0;
+}
+
+extern "C" void rados_nobjects_list_close(rados_list_ctx_t h)
+{
+  tracepoint(librados, rados_nobjects_list_close_enter, h);
+  librados::ObjListCtx *lh = (librados::ObjListCtx *)h;
+  delete lh;
+  tracepoint(librados, rados_nobjects_list_close_exit);
+}
+
+extern "C" uint32_t rados_nobjects_list_seek(rados_list_ctx_t listctx,
+                                           uint32_t pos)
+{
+  librados::ObjListCtx *lh = (librados::ObjListCtx *)listctx;
+  tracepoint(librados, rados_nobjects_list_seek_enter, listctx, pos);
+  uint32_t r = lh->ctx->nlist_seek(lh->nlc, pos);
+  tracepoint(librados, rados_nobjects_list_seek_exit, r);
+  return r;
+}
+
+extern "C" uint32_t rados_nobjects_list_seek_cursor(rados_list_ctx_t listctx,
+                                                    rados_object_list_cursor cursor)
+{
+  librados::ObjListCtx *lh = (librados::ObjListCtx *)listctx;
+
+  tracepoint(librados, rados_nobjects_list_seek_cursor_enter, listctx);
+  uint32_t r = lh->ctx->nlist_seek(lh->nlc, cursor);
+  tracepoint(librados, rados_nobjects_list_seek_cursor_exit, r);
+  return r;
+}
+
+extern "C" int rados_nobjects_list_get_cursor(rados_list_ctx_t listctx,
+                                              rados_object_list_cursor *cursor)
+{
+  librados::ObjListCtx *lh = (librados::ObjListCtx *)listctx;
+
+  tracepoint(librados, rados_nobjects_list_get_cursor_enter, listctx);
+  *cursor = lh->ctx->nlist_get_cursor(lh->nlc);
+  tracepoint(librados, rados_nobjects_list_get_cursor_exit, 0);
+  return 0;
+}
+
+extern "C" uint32_t rados_nobjects_list_get_pg_hash_position(
+  rados_list_ctx_t listctx)
+{
+  librados::ObjListCtx *lh = (librados::ObjListCtx *)listctx;
+  tracepoint(librados, rados_nobjects_list_get_pg_hash_position_enter, listctx);
+  uint32_t retval = lh->nlc->get_pg_hash_position();
+  tracepoint(librados, rados_nobjects_list_get_pg_hash_position_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_nobjects_list_next(rados_list_ctx_t listctx, const char **entry, const char **key, const char **nspace)
+{
+  tracepoint(librados, rados_nobjects_list_next_enter, listctx);
+  librados::ObjListCtx *lh = (librados::ObjListCtx *)listctx;
+  Objecter::NListContext *h = lh->nlc;
+
+  // if the list is non-empty, this method has been called before
+  if (!h->list.empty())
+    // so let's kill the previously-returned object
+    h->list.pop_front();
+
+  if (h->list.empty()) {
+    int ret = lh->ctx->nlist(lh->nlc, RADOS_LIST_MAX_ENTRIES);
+    if (ret < 0) {
+      tracepoint(librados, rados_nobjects_list_next_exit, ret, NULL, NULL, NULL);
+      return ret;
+    }
+    if (h->list.empty()) {
+      tracepoint(librados, rados_nobjects_list_next_exit, -ENOENT, NULL, NULL, NULL);
+      return -ENOENT;
+    }
+  }
+
+  *entry = h->list.front().oid.c_str();
+
+  if (key) {
+    if (h->list.front().locator.size())
+      *key = h->list.front().locator.c_str();
+    else
+      *key = NULL;
+  }
+  if (nspace)
+    *nspace = h->list.front().nspace.c_str();
+  tracepoint(librados, rados_nobjects_list_next_exit, 0, *entry, key, nspace);
+  return 0;
+}
+
+
+/*
+ * removed legacy v2 list objects stubs
+ *
+ * thse return -ENOTSUP where possible.
+ */
+extern "C" int rados_objects_list_open(
+  rados_ioctx_t io,
+  rados_list_ctx_t *ctx)
+{
+  return -ENOTSUP;
+}
+
+extern "C" uint32_t rados_objects_list_get_pg_hash_position(
+  rados_list_ctx_t ctx)
+{
+  return 0;
+}
+
+extern "C" uint32_t rados_objects_list_seek(
+  rados_list_ctx_t ctx,
+  uint32_t pos)
+{
+  return 0;
+}
+
+extern "C" int rados_objects_list_next(
+  rados_list_ctx_t ctx,
+  const char **entry,
+  const char **key)
+{
+  return -ENOTSUP;
+}
+
+extern "C" void rados_objects_list_close(
+  rados_list_ctx_t ctx)
+{
+}
+
+
+// -------------------------
+// aio
+
+extern "C" int rados_aio_create_completion(void *cb_arg,
+                                          rados_callback_t cb_complete,
+                                          rados_callback_t cb_safe,
+                                          rados_completion_t *pc)
+{
+  tracepoint(librados, rados_aio_create_completion_enter, cb_arg, cb_complete, cb_safe);
+  librados::AioCompletionImpl *c = new librados::AioCompletionImpl;
+  if (cb_complete)
+    c->set_complete_callback(cb_arg, cb_complete);
+  if (cb_safe)
+    c->set_safe_callback(cb_arg, cb_safe);
+  *pc = c;
+  tracepoint(librados, rados_aio_create_completion_exit, 0, *pc);
+  return 0;
+}
+
+extern "C" int rados_aio_wait_for_complete(rados_completion_t c)
+{
+  tracepoint(librados, rados_aio_wait_for_complete_enter, c);
+  int retval = ((librados::AioCompletionImpl*)c)->wait_for_complete();
+  tracepoint(librados, rados_aio_wait_for_complete_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_aio_wait_for_safe(rados_completion_t c)
+{
+  tracepoint(librados, rados_aio_wait_for_safe_enter, c);
+  int retval = ((librados::AioCompletionImpl*)c)->wait_for_safe();
+  tracepoint(librados, rados_aio_wait_for_safe_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_aio_is_complete(rados_completion_t c)
+{
+  tracepoint(librados, rados_aio_is_complete_enter, c);
+  int retval = ((librados::AioCompletionImpl*)c)->is_complete();
+  tracepoint(librados, rados_aio_is_complete_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_aio_is_safe(rados_completion_t c)
+{
+  tracepoint(librados, rados_aio_is_safe_enter, c);
+  int retval = ((librados::AioCompletionImpl*)c)->is_safe();
+  tracepoint(librados, rados_aio_is_safe_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_aio_wait_for_complete_and_cb(rados_completion_t c)
+{
+  tracepoint(librados, rados_aio_wait_for_complete_and_cb_enter, c);
+  int retval = ((librados::AioCompletionImpl*)c)->wait_for_complete_and_cb();
+  tracepoint(librados, rados_aio_wait_for_complete_and_cb_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_aio_wait_for_safe_and_cb(rados_completion_t c)
+{
+  tracepoint(librados, rados_aio_wait_for_safe_and_cb_enter, c);
+  int retval = ((librados::AioCompletionImpl*)c)->wait_for_safe_and_cb();
+  tracepoint(librados, rados_aio_wait_for_safe_and_cb_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_aio_is_complete_and_cb(rados_completion_t c)
+{
+  tracepoint(librados, rados_aio_is_complete_and_cb_enter, c);
+  int retval = ((librados::AioCompletionImpl*)c)->is_complete_and_cb();
+  tracepoint(librados, rados_aio_is_complete_and_cb_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_aio_is_safe_and_cb(rados_completion_t c)
+{
+  tracepoint(librados, rados_aio_is_safe_and_cb_enter, c);
+  int retval = ((librados::AioCompletionImpl*)c)->is_safe_and_cb();
+  tracepoint(librados, rados_aio_is_safe_and_cb_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_aio_get_return_value(rados_completion_t c)
+{
+  tracepoint(librados, rados_aio_get_return_value_enter, c);
+  int retval = ((librados::AioCompletionImpl*)c)->get_return_value();
+  tracepoint(librados, rados_aio_get_return_value_exit, retval);
+  return retval;
+}
+
+extern "C" uint64_t rados_aio_get_version(rados_completion_t c)
+{
+  tracepoint(librados, rados_aio_get_version_enter, c);
+  uint64_t retval = ((librados::AioCompletionImpl*)c)->get_version();
+  tracepoint(librados, rados_aio_get_version_exit, retval);
+  return retval;
+}
+
+extern "C" void rados_aio_release(rados_completion_t c)
+{
+  tracepoint(librados, rados_aio_release_enter, c);
+  ((librados::AioCompletionImpl*)c)->put();
+  tracepoint(librados, rados_aio_release_exit);
+}
+
+extern "C" int rados_aio_read(rados_ioctx_t io, const char *o,
+                              rados_completion_t completion,
+                              char *buf, size_t len, uint64_t off)
+{
+  tracepoint(librados, rados_aio_read_enter, io, o, completion, len, off);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  object_t oid(o);
+  int retval = ctx->aio_read(oid, (librados::AioCompletionImpl*)completion,
+                      buf, len, off, ctx->snap_seq);
+  tracepoint(librados, rados_aio_read_exit, retval);
+  return retval;
+}
+
+#ifdef WITH_BLKIN
+extern "C" int rados_aio_read_traced(rados_ioctx_t io, const char *o,
+                                    rados_completion_t completion,
+                                    char *buf, size_t len, uint64_t off,
+                                    struct blkin_trace_info *info)
+{
+  tracepoint(librados, rados_aio_read_enter, io, o, completion, len, off);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  object_t oid(o);
+  int retval = ctx->aio_read(oid, (librados::AioCompletionImpl*)completion,
+                             buf, len, off, ctx->snap_seq, info);
+  tracepoint(librados, rados_aio_read_exit, retval);
+  return retval;
+}
+#endif
+
+extern "C" int rados_aio_write(rados_ioctx_t io, const char *o,
+                               rados_completion_t completion,
+                               const char *buf, size_t len, uint64_t off)
+{
+  tracepoint(librados, rados_aio_write_enter, io, o, completion, buf, len, off);
+  if (len > UINT_MAX/2)
+    return -E2BIG;
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  object_t oid(o);
+  bufferlist bl;
+  bl.append(buf, len);
+  int retval = ctx->aio_write(oid, (librados::AioCompletionImpl*)completion,
+                       bl, len, off);
+  tracepoint(librados, rados_aio_write_exit, retval);
+  return retval;
+}
+
+#ifdef WITH_BLKIN
+extern "C" int rados_aio_write_traced(rados_ioctx_t io, const char *o,
+                                      rados_completion_t completion,
+                                      const char *buf, size_t len, uint64_t off,
+                                      struct blkin_trace_info *info)
+{
+  tracepoint(librados, rados_aio_write_enter, io, o, completion, buf, len, off);
+  if (len > UINT_MAX/2)
+    return -E2BIG;
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  object_t oid(o);
+  bufferlist bl;
+  bl.append(buf, len);
+  int retval = ctx->aio_write(oid, (librados::AioCompletionImpl*)completion,
+                              bl, len, off, info);
+  tracepoint(librados, rados_aio_write_exit, retval);
+  return retval;
+}
+#endif
+
+extern "C" int rados_aio_append(rados_ioctx_t io, const char *o,
+                               rados_completion_t completion,
+                               const char *buf, size_t len)
+{
+  tracepoint(librados, rados_aio_append_enter, io, o, completion, buf, len);
+  if (len > UINT_MAX/2)
+    return -E2BIG;
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  object_t oid(o);
+  bufferlist bl;
+  bl.append(buf, len);
+  int retval = ctx->aio_append(oid, (librados::AioCompletionImpl*)completion,
+                        bl, len);
+  tracepoint(librados, rados_aio_append_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_aio_write_full(rados_ioctx_t io, const char *o,
+                                   rados_completion_t completion,
+                                   const char *buf, size_t len)
+{
+  tracepoint(librados, rados_aio_write_full_enter, io, o, completion, buf, len);
+  if (len > UINT_MAX/2)
+    return -E2BIG;
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  object_t oid(o);
+  bufferlist bl;
+  bl.append(buf, len);
+  int retval = ctx->aio_write_full(oid, (librados::AioCompletionImpl*)completion, bl);
+  tracepoint(librados, rados_aio_write_full_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_aio_writesame(rados_ioctx_t io, const char *o,
+                                  rados_completion_t completion,
+                                  const char *buf, size_t data_len,
+                                  size_t write_len, uint64_t off)
+{
+  tracepoint(librados, rados_aio_writesame_enter, io, o, completion, buf,
+                                               data_len, write_len, off);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  object_t oid(o);
+  bufferlist bl;
+  bl.append(buf, data_len);
+  int retval = ctx->aio_writesame(o, (librados::AioCompletionImpl*)completion,
+                                 bl, write_len, off);
+  tracepoint(librados, rados_aio_writesame_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_aio_remove(rados_ioctx_t io, const char *o,
+                               rados_completion_t completion)
+{
+  tracepoint(librados, rados_aio_remove_enter, io, o, completion);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  object_t oid(o);
+  int retval = ctx->aio_remove(oid, (librados::AioCompletionImpl*)completion);
+  tracepoint(librados, rados_aio_remove_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_aio_flush_async(rados_ioctx_t io,
+                                    rados_completion_t completion)
+{
+  tracepoint(librados, rados_aio_flush_async_enter, io, completion);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  ctx->flush_aio_writes_async((librados::AioCompletionImpl*)completion);
+  tracepoint(librados, rados_aio_flush_async_exit, 0);
+  return 0;
+}
+
+extern "C" int rados_aio_flush(rados_ioctx_t io)
+{
+  tracepoint(librados, rados_aio_flush_enter, io);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  ctx->flush_aio_writes();
+  tracepoint(librados, rados_aio_flush_exit, 0);
+  return 0;
+}
+
+struct AioGetxattrData {
+  AioGetxattrData(char* buf, rados_completion_t c, size_t l) :
+    user_buf(buf), len(l), user_completion((librados::AioCompletionImpl*)c) {}
+  bufferlist bl;
+  char* user_buf;
+  size_t len;
+  struct librados::C_AioCompleteAndSafe user_completion;
+};
+
+static void rados_aio_getxattr_complete(rados_completion_t c, void *arg) {
+  AioGetxattrData *cdata = reinterpret_cast<AioGetxattrData*>(arg);
+  int rc = rados_aio_get_return_value(c);
+  if (rc >= 0) {
+    if (cdata->bl.length() > cdata->len) {
+      rc = -ERANGE;
+    } else {
+      if (!cdata->bl.is_provided_buffer(cdata->user_buf))
+       cdata->bl.copy(0, cdata->bl.length(), cdata->user_buf);
+      rc = cdata->bl.length();
+    }
+  }
+  cdata->user_completion.finish(rc);
+  delete cdata;
+}
+
+extern "C" int rados_aio_getxattr(rados_ioctx_t io, const char *o,
+                                 rados_completion_t completion,
+                                 const char *name, char *buf, size_t len)
+{
+  tracepoint(librados, rados_aio_getxattr_enter, io, o, completion, name, len);
+  // create data object to be passed to async callback
+  AioGetxattrData *cdata = new AioGetxattrData(buf, completion, len);
+  if (!cdata) {
+    tracepoint(librados, rados_aio_getxattr_exit, -ENOMEM, NULL, 0);
+    return -ENOMEM;
+  }
+  cdata->bl.push_back(buffer::create_static(len, buf));
+  // create completion callback
+  librados::AioCompletionImpl *c = new librados::AioCompletionImpl;
+  c->set_complete_callback(cdata, rados_aio_getxattr_complete);
+  // call async getxattr of IoCtx
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  object_t oid(o);
+  int ret = ctx->aio_getxattr(oid, c, name, cdata->bl);
+  tracepoint(librados, rados_aio_getxattr_exit, ret, buf, ret);
+  return ret;
+}
+
+namespace {
+struct AioGetxattrsData {
+  AioGetxattrsData(rados_completion_t c, rados_xattrs_iter_t *_iter) :
+    iter(_iter), user_completion((librados::AioCompletionImpl*)c) {
+    it = new librados::RadosXattrsIter();
+  }
+  ~AioGetxattrsData() {
+    if (it) delete it;
+  }
+  librados::RadosXattrsIter *it;
+  rados_xattrs_iter_t *iter;
+  struct librados::C_AioCompleteAndSafe user_completion;
+};
+}
+
+static void rados_aio_getxattrs_complete(rados_completion_t c, void *arg) {
+  AioGetxattrsData *cdata = reinterpret_cast<AioGetxattrsData*>(arg);
+  int rc = rados_aio_get_return_value(c);
+  if (rc) {
+    cdata->user_completion.finish(rc);
+  } else {
+    cdata->it->i = cdata->it->attrset.begin();
+    *cdata->iter = cdata->it;
+    cdata->it = 0;
+    cdata->user_completion.finish(0);
+  }
+  delete cdata;
+}
+
+extern "C" int rados_aio_getxattrs(rados_ioctx_t io, const char *oid,
+                                  rados_completion_t completion,
+                                  rados_xattrs_iter_t *iter)
+{
+  tracepoint(librados, rados_aio_getxattrs_enter, io, oid, completion);
+  // create data object to be passed to async callback
+  AioGetxattrsData *cdata = new AioGetxattrsData(completion, iter);
+  if (!cdata) {
+    tracepoint(librados, rados_getxattrs_exit, -ENOMEM, NULL);
+    return -ENOMEM;
+  }
+  // create completion callback
+  librados::AioCompletionImpl *c = new librados::AioCompletionImpl;
+  c->set_complete_callback(cdata, rados_aio_getxattrs_complete);
+  // call async getxattrs of IoCtx
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  object_t obj(oid);
+  int ret = ctx->aio_getxattrs(obj, c, cdata->it->attrset);
+  tracepoint(librados, rados_aio_getxattrs_exit, ret, cdata->it);
+  return ret;
+}
+
+extern "C" int rados_aio_setxattr(rados_ioctx_t io, const char *o,
+                                 rados_completion_t completion,
+                                 const char *name, const char *buf, size_t len)
+{
+  tracepoint(librados, rados_aio_setxattr_enter, io, o, completion, name, buf, len);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  object_t oid(o);
+  bufferlist bl;
+  bl.append(buf, len);
+  int retval = ctx->aio_setxattr(oid, (librados::AioCompletionImpl*)completion, name, bl);
+  tracepoint(librados, rados_aio_setxattr_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_aio_rmxattr(rados_ioctx_t io, const char *o,
+                                rados_completion_t completion,
+                                const char *name)
+{
+  tracepoint(librados, rados_aio_rmxattr_enter, io, o, completion, name);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  object_t oid(o);
+  int retval = ctx->aio_rmxattr(oid, (librados::AioCompletionImpl*)completion, name);
+  tracepoint(librados, rados_aio_rmxattr_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_aio_stat(rados_ioctx_t io, const char *o,
+                             rados_completion_t completion,
+                             uint64_t *psize, time_t *pmtime)
+{
+  tracepoint(librados, rados_aio_stat_enter, io, o, completion);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  object_t oid(o);
+  int retval = ctx->aio_stat(oid, (librados::AioCompletionImpl*)completion,
+                      psize, pmtime);
+  tracepoint(librados, rados_aio_stat_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_aio_cmpext(rados_ioctx_t io, const char *o,
+                               rados_completion_t completion, const char *cmp_buf,
+                               size_t cmp_len, uint64_t off)
+{
+  tracepoint(librados, rados_aio_cmpext_enter, io, o, completion, cmp_buf,
+            cmp_len, off);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  object_t oid(o);
+  int retval = ctx->aio_cmpext(oid, (librados::AioCompletionImpl*)completion,
+                              cmp_buf, cmp_len, off);
+  tracepoint(librados, rados_aio_cmpext_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_aio_cancel(rados_ioctx_t io, rados_completion_t completion)
+{
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  return ctx->aio_cancel((librados::AioCompletionImpl*)completion);
+}
+
+extern "C" int rados_aio_exec(rados_ioctx_t io, const char *o,
+                             rados_completion_t completion,
+                             const char *cls, const char *method,
+                             const char *inbuf, size_t in_len,
+                             char *buf, size_t out_len)
+{
+  tracepoint(librados, rados_aio_exec_enter, io, o, completion);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  object_t oid(o);
+  bufferlist inbl;
+  inbl.append(inbuf, in_len);
+  int retval = ctx->aio_exec(oid, (librados::AioCompletionImpl*)completion,
+                      cls, method, inbl, buf, out_len);
+  tracepoint(librados, rados_aio_exec_exit, retval);
+  return retval;
+}
+
+struct C_WatchCB : public librados::WatchCtx {
+  rados_watchcb_t wcb;
+  void *arg;
+  C_WatchCB(rados_watchcb_t _wcb, void *_arg) : wcb(_wcb), arg(_arg) {}
+  void notify(uint8_t opcode, uint64_t ver, bufferlist& bl) override {
+    wcb(opcode, ver, arg);
+  }
+};
+
+extern "C" int rados_watch(rados_ioctx_t io, const char *o, uint64_t ver,
+                          uint64_t *handle,
+                          rados_watchcb_t watchcb, void *arg)
+{
+  tracepoint(librados, rados_watch_enter, io, o, ver, watchcb, arg);
+  uint64_t *cookie = handle;
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  object_t oid(o);
+  C_WatchCB *wc = new C_WatchCB(watchcb, arg);
+  int retval = ctx->watch(oid, cookie, wc, NULL, true);
+  tracepoint(librados, rados_watch_exit, retval, *handle);
+  return retval;
+}
+
+struct C_WatchCB2 : public librados::WatchCtx2 {
+  rados_watchcb2_t wcb;
+  rados_watcherrcb_t errcb;
+  void *arg;
+  C_WatchCB2(rados_watchcb2_t _wcb,
+            rados_watcherrcb_t _errcb,
+            void *_arg) : wcb(_wcb), errcb(_errcb), arg(_arg) {}
+  void handle_notify(uint64_t notify_id,
+                    uint64_t cookie,
+                    uint64_t notifier_gid,
+                    bufferlist& bl) override {
+    wcb(arg, notify_id, cookie, notifier_gid, bl.c_str(), bl.length());
+  }
+  void handle_error(uint64_t cookie, int err) override {
+    if (errcb)
+      errcb(arg, cookie, err);
+  }
+};
+
+extern "C" int rados_watch2(rados_ioctx_t io, const char *o, uint64_t *handle,
+                           rados_watchcb2_t watchcb,
+                           rados_watcherrcb_t watcherrcb,
+                           void *arg) {
+  return rados_watch3(io, o, handle, watchcb, watcherrcb, 0, arg);
+}
+
+extern "C" int rados_watch3(rados_ioctx_t io, const char *o, uint64_t *handle,
+                           rados_watchcb2_t watchcb,
+                           rados_watcherrcb_t watcherrcb,
+                           uint32_t timeout,
+                           void *arg)
+{
+  tracepoint(librados, rados_watch3_enter, io, o, handle, watchcb, timeout, arg);
+  int ret;
+  if (!watchcb || !o || !handle) {
+    ret = -EINVAL;
+  } else {
+    uint64_t *cookie = handle;
+    librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+    object_t oid(o);
+    C_WatchCB2 *wc = new C_WatchCB2(watchcb, watcherrcb, arg);
+    ret = ctx->watch(oid, cookie, NULL, wc, timeout, true);
+  }
+  tracepoint(librados, rados_watch3_exit, ret, handle ? *handle : 0);
+  return ret;
+}
+
+extern "C" int rados_aio_watch(rados_ioctx_t io, const char *o,
+                               rados_completion_t completion,
+                               uint64_t *handle,
+                               rados_watchcb2_t watchcb,
+                               rados_watcherrcb_t watcherrcb, void *arg) {
+  return rados_aio_watch2(io, o, completion, handle, watchcb, watcherrcb, 0, arg);
+}
+
+extern "C" int rados_aio_watch2(rados_ioctx_t io, const char *o,
+                                rados_completion_t completion,
+                                uint64_t *handle,
+                                rados_watchcb2_t watchcb,
+                                rados_watcherrcb_t watcherrcb,
+                                uint32_t timeout, void *arg)
+{
+  tracepoint(librados, rados_aio_watch2_enter, io, o, completion, handle, watchcb, timeout, arg);
+  int ret;
+  if (!completion || !watchcb || !o || !handle) {
+    ret = -EINVAL;
+  } else {
+    uint64_t *cookie = handle;
+    librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+    object_t oid(o);
+    librados::AioCompletionImpl *c =
+      reinterpret_cast<librados::AioCompletionImpl*>(completion);
+    C_WatchCB2 *wc = new C_WatchCB2(watchcb, watcherrcb, arg);
+    ret = ctx->aio_watch(oid, c, cookie, NULL, wc, timeout, true);
+  }
+  tracepoint(librados, rados_aio_watch2_exit, ret, handle ? *handle : 0);
+  return ret;
+}
+
+
+extern "C" int rados_unwatch(rados_ioctx_t io, const char *o, uint64_t handle)
+{
+  tracepoint(librados, rados_unwatch_enter, io, o, handle);
+  uint64_t cookie = handle;
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  int retval = ctx->unwatch(cookie);
+  tracepoint(librados, rados_unwatch_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_unwatch2(rados_ioctx_t io, uint64_t handle)
+{
+  tracepoint(librados, rados_unwatch2_enter, io, handle);
+  uint64_t cookie = handle;
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  int retval = ctx->unwatch(cookie);
+  tracepoint(librados, rados_unwatch2_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_aio_unwatch(rados_ioctx_t io, uint64_t handle,
+                                 rados_completion_t completion)
+{
+  tracepoint(librados, rados_aio_unwatch_enter, io, handle, completion);
+  uint64_t cookie = handle;
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  librados::AioCompletionImpl *c =
+    reinterpret_cast<librados::AioCompletionImpl*>(completion);
+  int retval = ctx->aio_unwatch(cookie, c);
+  tracepoint(librados, rados_aio_unwatch_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_watch_check(rados_ioctx_t io, uint64_t handle)
+{
+  tracepoint(librados, rados_watch_check_enter, io, handle);
+  uint64_t cookie = handle;
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  int retval = ctx->watch_check(cookie);
+  tracepoint(librados, rados_watch_check_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_notify(rados_ioctx_t io, const char *o,
+                           uint64_t ver, const char *buf, int buf_len)
+{
+  tracepoint(librados, rados_notify_enter, io, o, ver, buf, buf_len);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  object_t oid(o);
+  bufferlist bl;
+  if (buf) {
+    bufferptr p = buffer::create(buf_len);
+    memcpy(p.c_str(), buf, buf_len);
+    bl.push_back(p);
+  }
+  int retval = ctx->notify(oid, bl, 0, NULL, NULL, NULL);
+  tracepoint(librados, rados_notify_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_notify2(rados_ioctx_t io, const char *o,
+                            const char *buf, int buf_len,
+                            uint64_t timeout_ms,
+                            char **reply_buffer,
+                            size_t *reply_buffer_len)
+{
+  tracepoint(librados, rados_notify2_enter, io, o, buf, buf_len, timeout_ms);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  object_t oid(o);
+  bufferlist bl;
+  if (buf) {
+    bufferptr p = buffer::create(buf_len);
+    memcpy(p.c_str(), buf, buf_len);
+    bl.push_back(p);
+  }
+  int ret = ctx->notify(oid, bl, timeout_ms, NULL, reply_buffer, reply_buffer_len);
+  tracepoint(librados, rados_notify2_exit, ret);
+  return ret;
+}
+
+extern "C" int rados_aio_notify(rados_ioctx_t io, const char *o,
+                                rados_completion_t completion,
+                                const char *buf, int buf_len,
+                                uint64_t timeout_ms, char **reply_buffer,
+                                size_t *reply_buffer_len)
+{
+  tracepoint(librados, rados_aio_notify_enter, io, o, completion, buf, buf_len,
+             timeout_ms);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  object_t oid(o);
+  bufferlist bl;
+  if (buf) {
+    bl.push_back(buffer::copy(buf, buf_len));
+  }
+  librados::AioCompletionImpl *c =
+    reinterpret_cast<librados::AioCompletionImpl*>(completion);
+  int ret = ctx->aio_notify(oid, c, bl, timeout_ms, NULL, reply_buffer,
+                            reply_buffer_len);
+  tracepoint(librados, rados_aio_notify_exit, ret);
+  return ret;
+}
+
+extern "C" int rados_notify_ack(rados_ioctx_t io, const char *o,
+                               uint64_t notify_id, uint64_t handle,
+                               const char *buf, int buf_len)
+{
+  tracepoint(librados, rados_notify_ack_enter, io, o, notify_id, handle, buf, buf_len);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  object_t oid(o);
+  bufferlist bl;
+  if (buf) {
+    bufferptr p = buffer::create(buf_len);
+    memcpy(p.c_str(), buf, buf_len);
+    bl.push_back(p);
+  }
+  ctx->notify_ack(oid, notify_id, handle, bl);
+  tracepoint(librados, rados_notify_ack_exit, 0);
+  return 0;
+}
+
+extern "C" int rados_watch_flush(rados_t cluster)
+{
+  tracepoint(librados, rados_watch_flush_enter, cluster);
+  librados::RadosClient *client = (librados::RadosClient *)cluster;
+  int retval = client->watch_flush();
+  tracepoint(librados, rados_watch_flush_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_aio_watch_flush(rados_t cluster, rados_completion_t completion)
+{
+  tracepoint(librados, rados_aio_watch_flush_enter, cluster, completion);
+  librados::RadosClient *client = (librados::RadosClient *)cluster;
+  librados::AioCompletionImpl *c = (librados::AioCompletionImpl*)completion;
+  int retval = client->async_watch_flush(c);
+  tracepoint(librados, rados_aio_watch_flush_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_set_alloc_hint(rados_ioctx_t io, const char *o,
+                                    uint64_t expected_object_size,
+                                    uint64_t expected_write_size)
+{
+  tracepoint(librados, rados_set_alloc_hint_enter, io, o, expected_object_size, expected_write_size);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  object_t oid(o);
+  int retval = ctx->set_alloc_hint(oid, expected_object_size,
+                                  expected_write_size, 0);
+  tracepoint(librados, rados_set_alloc_hint_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_set_alloc_hint2(rados_ioctx_t io, const char *o,
+                                    uint64_t expected_object_size,
+                                    uint64_t expected_write_size,
+                                    uint32_t flags)
+{
+  tracepoint(librados, rados_set_alloc_hint2_enter, io, o, expected_object_size, expected_write_size, flags);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  object_t oid(o);
+  int retval = ctx->set_alloc_hint(oid, expected_object_size,
+                                  expected_write_size, flags);
+  tracepoint(librados, rados_set_alloc_hint2_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_lock_exclusive(rados_ioctx_t io, const char * o,
+                         const char * name, const char * cookie,
+                         const char * desc, struct timeval * duration,
+                         uint8_t flags)
+{
+  tracepoint(librados, rados_lock_exclusive_enter, io, o, name, cookie, desc, duration, flags);
+  librados::IoCtx ctx;
+  librados::IoCtx::from_rados_ioctx_t(io, ctx);
+
+  int retval = ctx.lock_exclusive(o, name, cookie, desc, duration, flags);
+  tracepoint(librados, rados_lock_exclusive_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_lock_shared(rados_ioctx_t io, const char * o,
+                         const char * name, const char * cookie,
+                         const char * tag, const char * desc,
+                         struct timeval * duration, uint8_t flags)
+{
+  tracepoint(librados, rados_lock_shared_enter, io, o, name, cookie, tag, desc, duration, flags);
+  librados::IoCtx ctx;
+  librados::IoCtx::from_rados_ioctx_t(io, ctx);
+
+  int retval = ctx.lock_shared(o, name, cookie, tag, desc, duration, flags);
+  tracepoint(librados, rados_lock_shared_exit, retval);
+  return retval;
+}
+extern "C" int rados_unlock(rados_ioctx_t io, const char *o, const char *name,
+                           const char *cookie)
+{
+  tracepoint(librados, rados_unlock_enter, io, o, name, cookie);
+  librados::IoCtx ctx;
+  librados::IoCtx::from_rados_ioctx_t(io, ctx);
+
+  int retval = ctx.unlock(o, name, cookie);
+  tracepoint(librados, rados_unlock_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_aio_unlock(rados_ioctx_t io, const char *o, const char *name,
+                               const char *cookie, rados_completion_t completion)
+{
+  tracepoint(librados, rados_aio_unlock_enter, io, o, name, cookie, completion);
+  librados::IoCtx ctx;
+  librados::IoCtx::from_rados_ioctx_t(io, ctx);
+  librados::AioCompletionImpl *comp = (librados::AioCompletionImpl*)completion;
+  librados::AioCompletion c(comp);
+  int retval = ctx.aio_unlock(o, name, cookie, &c);
+  tracepoint(librados, rados_aio_unlock_exit, retval);
+  return retval;
+}
+
+extern "C" ssize_t rados_list_lockers(rados_ioctx_t io, const char *o,
+                                     const char *name, int *exclusive,
+                                     char *tag, size_t *tag_len,
+                                     char *clients, size_t *clients_len,
+                                     char *cookies, size_t *cookies_len,
+                                     char *addrs, size_t *addrs_len)
+{
+  tracepoint(librados, rados_list_lockers_enter, io, o, name, *tag_len, *clients_len, *cookies_len, *addrs_len);
+  librados::IoCtx ctx;
+  librados::IoCtx::from_rados_ioctx_t(io, ctx);
+  std::string name_str = name;
+  std::string oid = o;
+  std::string tag_str;
+  int tmp_exclusive;
+  std::list<librados::locker_t> lockers;
+  int r = ctx.list_lockers(oid, name_str, &tmp_exclusive, &tag_str, &lockers);
+  if (r < 0) {
+    tracepoint(librados, rados_list_lockers_exit, r, *exclusive, "", *tag_len, *clients_len, *cookies_len, *addrs_len);
+         return r;
+  }
+
+  size_t clients_total = 0;
+  size_t cookies_total = 0;
+  size_t addrs_total = 0;
+  list<librados::locker_t>::const_iterator it;
+  for (it = lockers.begin(); it != lockers.end(); ++it) {
+    clients_total += it->client.length() + 1;
+    cookies_total += it->cookie.length() + 1;
+    addrs_total += it->address.length() + 1;
+  }
+
+  bool too_short = ((clients_total > *clients_len) ||
+                    (cookies_total > *cookies_len) ||
+                    (addrs_total > *addrs_len) ||
+                    (tag_str.length() + 1 > *tag_len));
+  *clients_len = clients_total;
+  *cookies_len = cookies_total;
+  *addrs_len = addrs_total;
+  *tag_len = tag_str.length() + 1;
+  if (too_short) {
+    tracepoint(librados, rados_list_lockers_exit, -ERANGE, *exclusive, "", *tag_len, *clients_len, *cookies_len, *addrs_len);
+    return -ERANGE;
+  }
+
+  strcpy(tag, tag_str.c_str());
+  char *clients_p = clients;
+  char *cookies_p = cookies;
+  char *addrs_p = addrs;
+  for (it = lockers.begin(); it != lockers.end(); ++it) {
+    strcpy(clients_p, it->client.c_str());
+    strcpy(cookies_p, it->cookie.c_str());
+    strcpy(addrs_p, it->address.c_str());
+    tracepoint(librados, rados_list_lockers_locker, clients_p, cookies_p, addrs_p);
+    clients_p += it->client.length() + 1;
+    cookies_p += it->cookie.length() + 1;
+    addrs_p += it->address.length() + 1;
+  }
+  if (tmp_exclusive)
+    *exclusive = 1;
+  else
+    *exclusive = 0;
+
+  int retval = lockers.size();
+  tracepoint(librados, rados_list_lockers_exit, retval, *exclusive, tag, *tag_len, *clients_len, *cookies_len, *addrs_len);
+  return retval;
+}
+
+extern "C" int rados_break_lock(rados_ioctx_t io, const char *o,
+                               const char *name, const char *client,
+                               const char *cookie)
+{
+  tracepoint(librados, rados_break_lock_enter, io, o, name, client, cookie);
+  librados::IoCtx ctx;
+  librados::IoCtx::from_rados_ioctx_t(io, ctx);
+
+  int retval = ctx.break_lock(o, name, client, cookie);
+  tracepoint(librados, rados_break_lock_exit, retval);
+  return retval;
+}
+
+extern "C" rados_write_op_t rados_create_write_op()
+{
+  tracepoint(librados, rados_create_write_op_enter);
+  rados_write_op_t retval = new (std::nothrow)::ObjectOperation;
+  tracepoint(librados, rados_create_write_op_exit, retval);
+  return retval;
+}
+
+extern "C" void rados_release_write_op(rados_write_op_t write_op)
+{
+  tracepoint(librados, rados_release_write_op_enter, write_op);
+  delete (::ObjectOperation*)write_op;
+  tracepoint(librados, rados_release_write_op_exit);
+}
+
+extern "C" void rados_write_op_set_flags(rados_write_op_t write_op, int flags)
+{
+  tracepoint(librados, rados_write_op_set_flags_enter, write_op, flags);
+  ((::ObjectOperation *)write_op)->set_last_op_flags(get_op_flags(flags));
+  tracepoint(librados, rados_write_op_set_flags_exit);
+}
+
+extern "C" void rados_write_op_assert_version(rados_write_op_t write_op, uint64_t ver)
+{
+  tracepoint(librados, rados_write_op_assert_version_enter, write_op, ver);
+  ((::ObjectOperation *)write_op)->assert_version(ver);
+  tracepoint(librados, rados_write_op_assert_version_exit);
+}
+
+extern "C" void rados_write_op_assert_exists(rados_write_op_t write_op)
+{
+  tracepoint(librados, rados_write_op_assert_exists_enter, write_op);
+  ((::ObjectOperation *)write_op)->stat(NULL, (ceph::real_time *)NULL, NULL);
+  tracepoint(librados, rados_write_op_assert_exists_exit);
+}
+
+extern "C" void rados_write_op_cmpext(rados_write_op_t write_op,
+                                     const char *cmp_buf,
+                                     size_t cmp_len,
+                                     uint64_t off,
+                                     int *prval)
+{
+  tracepoint(librados, rados_write_op_cmpext_enter, write_op, cmp_buf,
+            cmp_len, off, prval);
+  ((::ObjectOperation *)write_op)->cmpext(off, cmp_len, cmp_buf, prval);
+  tracepoint(librados, rados_write_op_cmpext_exit);
+}
+
+extern "C" void rados_write_op_cmpxattr(rados_write_op_t write_op,
+                                       const char *name,
+                                      uint8_t comparison_operator,
+                                      const char *value,
+                                      size_t value_len)
+{
+  tracepoint(librados, rados_write_op_cmpxattr_enter, write_op, name, comparison_operator, value, value_len);
+  bufferlist bl;
+  bl.append(value, value_len);
+  ((::ObjectOperation *)write_op)->cmpxattr(name,
+                                           comparison_operator,
+                                           CEPH_OSD_CMPXATTR_MODE_STRING,
+                                           bl);
+  tracepoint(librados, rados_write_op_cmpxattr_exit);
+}
+
+static void rados_c_omap_cmp(ObjectOperation *op,
+                            const char *key,
+                            uint8_t comparison_operator,
+                            const char *val,
+                             size_t key_len,
+                            size_t val_len,
+                            int *prval)
+{
+  bufferlist bl;
+  bl.append(val, val_len);
+  std::map<std::string, pair<bufferlist, int> > assertions;
+  string lkey = string(key, key_len);
+
+  assertions[lkey] = 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)
+{
+  tracepoint(librados, rados_write_op_omap_cmp_enter, write_op, key, comparison_operator, val, val_len, prval);
+  rados_c_omap_cmp((::ObjectOperation *)write_op, key, comparison_operator,
+                   val, strlen(key), val_len, prval);
+  tracepoint(librados, rados_write_op_omap_cmp_exit);
+}
+
+extern "C" void rados_write_op_omap_cmp2(rados_write_op_t write_op,
+                                        const char *key,
+                                        uint8_t comparison_operator,
+                                        const char *val,
+                                        size_t key_len,
+                                        size_t val_len,
+                                        int *prval)
+{
+  tracepoint(librados, rados_write_op_omap_cmp_enter, write_op, key, comparison_operator, val, val_len, prval);
+  rados_c_omap_cmp((::ObjectOperation *)write_op, key, comparison_operator,
+                   val, key_len, val_len, prval);
+  tracepoint(librados, rados_write_op_omap_cmp_exit);
+}
+
+extern "C" void rados_write_op_setxattr(rados_write_op_t write_op,
+                                       const char *name,
+                                      const char *value,
+                                      size_t value_len)
+{
+  tracepoint(librados, rados_write_op_setxattr_enter, write_op, name, value, value_len);
+  bufferlist bl;
+  bl.append(value, value_len);
+  ((::ObjectOperation *)write_op)->setxattr(name, bl);
+  tracepoint(librados, rados_write_op_setxattr_exit);
+}
+
+extern "C" void rados_write_op_rmxattr(rados_write_op_t write_op,
+                                       const char *name)
+{
+  tracepoint(librados, rados_write_op_rmxattr_enter, write_op, name);
+  ((::ObjectOperation *)write_op)->rmxattr(name);
+  tracepoint(librados, rados_write_op_rmxattr_exit);
+}
+
+extern "C" void rados_write_op_create(rados_write_op_t write_op,
+                                      int exclusive,
+                                     const char* category) // unused
+{
+  tracepoint(librados, rados_write_op_create_enter, write_op, exclusive);
+  ::ObjectOperation *oo = (::ObjectOperation *) write_op;
+  oo->create(!!exclusive);
+  tracepoint(librados, rados_write_op_create_exit);
+}
+
+extern "C" void rados_write_op_write(rados_write_op_t write_op,
+                                    const char *buffer,
+                                    size_t len,
+                                     uint64_t offset)
+{
+  tracepoint(librados, rados_write_op_write_enter, write_op, buffer, len, offset);
+  bufferlist bl;
+  bl.append(buffer,len);
+  ((::ObjectOperation *)write_op)->write(offset, bl);
+  tracepoint(librados, rados_write_op_write_exit);
+}
+
+extern "C" void rados_write_op_write_full(rados_write_op_t write_op,
+                                         const char *buffer,
+                                         size_t len)
+{
+  tracepoint(librados, rados_write_op_write_full_enter, write_op, buffer, len);
+  bufferlist bl;
+  bl.append(buffer,len);
+  ((::ObjectOperation *)write_op)->write_full(bl);
+  tracepoint(librados, rados_write_op_write_full_exit);
+}
+
+extern "C" void rados_write_op_writesame(rados_write_op_t write_op,
+                                        const char *buffer,
+                                        size_t data_len,
+                                        size_t write_len,
+                                        uint64_t offset)
+{
+  tracepoint(librados, rados_write_op_writesame_enter, write_op, buffer, data_len, write_len, offset);
+  bufferlist bl;
+  bl.append(buffer, data_len);
+  ((::ObjectOperation *)write_op)->writesame(offset, write_len, bl);
+  tracepoint(librados, rados_write_op_writesame_exit);
+}
+
+extern "C" void rados_write_op_append(rados_write_op_t write_op,
+                                     const char *buffer,
+                                     size_t len)
+{
+  tracepoint(librados, rados_write_op_append_enter, write_op, buffer, len);
+  bufferlist bl;
+  bl.append(buffer,len);
+  ((::ObjectOperation *)write_op)->append(bl);
+  tracepoint(librados, rados_write_op_append_exit);
+}
+
+extern "C" void rados_write_op_remove(rados_write_op_t write_op)
+{
+  tracepoint(librados, rados_write_op_remove_enter, write_op);
+  ((::ObjectOperation *)write_op)->remove();
+  tracepoint(librados, rados_write_op_remove_exit);
+}
+
+extern "C" void rados_write_op_truncate(rados_write_op_t write_op,
+                                       uint64_t offset)
+{
+  tracepoint(librados, rados_write_op_truncate_enter, write_op, offset);
+  ((::ObjectOperation *)write_op)->truncate(offset);
+  tracepoint(librados, rados_write_op_truncate_exit);
+}
+
+extern "C" void rados_write_op_zero(rados_write_op_t write_op,
+                                   uint64_t offset,
+                                   uint64_t len)
+{
+  tracepoint(librados, rados_write_op_zero_enter, write_op, offset, len);
+  ((::ObjectOperation *)write_op)->zero(offset, len);
+  tracepoint(librados, rados_write_op_zero_exit);
+}
+
+extern "C" void rados_write_op_exec(rados_write_op_t write_op,
+                                   const char *cls,
+                                   const char *method,
+                                   const char *in_buf,
+                                   size_t in_len,
+                                   int *prval)
+{
+  tracepoint(librados, rados_write_op_exec_enter, write_op, cls, method, in_buf, in_len, prval);
+  bufferlist inbl;
+  inbl.append(in_buf, in_len);
+  ((::ObjectOperation *)write_op)->call(cls, method, inbl, NULL, NULL, prval);
+  tracepoint(librados, rados_write_op_exec_exit);
+}
+
+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)
+{
+  tracepoint(librados, rados_write_op_omap_set_enter, write_op, num);
+  std::map<std::string, bufferlist> entries;
+  for (size_t i = 0; i < num; ++i) {
+    tracepoint(librados, rados_write_op_omap_set_entry, keys[i], vals[i], lens[i]);
+    bufferlist bl(lens[i]);
+    bl.append(vals[i], lens[i]);
+    entries[keys[i]] = bl;
+  }
+  ((::ObjectOperation *)write_op)->omap_set(entries);
+  tracepoint(librados, rados_write_op_omap_set_exit);
+}
+
+extern "C" void rados_write_op_omap_set2(rados_write_op_t write_op,
+                                        char const* const* keys,
+                                        char const* const* vals,
+                                        const size_t *key_lens,
+                                        const size_t *val_lens,
+                                        size_t num)
+{
+  tracepoint(librados, rados_write_op_omap_set_enter, write_op, num);
+  std::map<std::string, bufferlist> entries;
+  for (size_t i = 0; i < num; ++i) {
+    bufferlist bl(val_lens[i]);
+    bl.append(vals[i], val_lens[i]);
+    string key(keys[i], key_lens[i]);
+    entries[key] = bl;
+  }
+  ((::ObjectOperation *)write_op)->omap_set(entries);
+  tracepoint(librados, rados_write_op_omap_set_exit);
+}
+
+extern "C" void rados_write_op_omap_rm_keys(rados_write_op_t write_op,
+                                            char const* const* keys,
+                                            size_t keys_len)
+{
+  tracepoint(librados, rados_write_op_omap_rm_keys_enter, write_op, keys_len);
+  for(size_t i = 0; i < keys_len; i++) {
+    tracepoint(librados, rados_write_op_omap_rm_keys_entry, keys[i]);
+  }
+  std::set<std::string> to_remove(keys, keys + keys_len);
+  ((::ObjectOperation *)write_op)->omap_rm_keys(to_remove);
+  tracepoint(librados, rados_write_op_omap_rm_keys_exit);
+}
+
+extern "C" void rados_write_op_omap_rm_keys2(rados_write_op_t write_op,
+                                            char const* const* keys,
+                                            const size_t* key_lens,
+                                            size_t keys_len)
+{
+  tracepoint(librados, rados_write_op_omap_rm_keys_enter, write_op, keys_len);
+  std::set<std::string> to_remove;
+  for(size_t i = 0; i < keys_len; i++) {
+    to_remove.emplace(keys[i], key_lens[i]);
+  }
+  ((::ObjectOperation *)write_op)->omap_rm_keys(to_remove);
+  tracepoint(librados, rados_write_op_omap_rm_keys_exit);
+}
+
+extern "C" void rados_write_op_omap_clear(rados_write_op_t write_op)
+{
+  tracepoint(librados, rados_write_op_omap_clear_enter, write_op);
+  ((::ObjectOperation *)write_op)->omap_clear();
+  tracepoint(librados, rados_write_op_omap_clear_exit);
+}
+
+extern "C" void rados_write_op_set_alloc_hint(rados_write_op_t write_op,
+                                            uint64_t expected_object_size,
+                                            uint64_t expected_write_size)
+{
+  tracepoint(librados, rados_write_op_set_alloc_hint_enter, write_op, expected_object_size, expected_write_size);
+  ((::ObjectOperation *)write_op)->set_alloc_hint(expected_object_size,
+                                                  expected_write_size, 0);
+  tracepoint(librados, rados_write_op_set_alloc_hint_exit);
+}
+
+extern "C" void rados_write_op_set_alloc_hint2(rados_write_op_t write_op,
+                                              uint64_t expected_object_size,
+                                              uint64_t expected_write_size,
+                                              uint32_t flags)
+{
+  tracepoint(librados, rados_write_op_set_alloc_hint2_enter, write_op, expected_object_size, expected_write_size, flags);
+  ((::ObjectOperation *)write_op)->set_alloc_hint(expected_object_size,
+                                                  expected_write_size,
+                                                 flags);
+  tracepoint(librados, rados_write_op_set_alloc_hint2_exit);
+}
+
+extern "C" int rados_write_op_operate(rados_write_op_t write_op,
+                                      rados_ioctx_t io,
+                                      const char *oid,
+                                     time_t *mtime,
+                                     int flags)
+{
+  tracepoint(librados, rados_write_op_operate_enter, write_op, io, oid, mtime, flags);
+  object_t obj(oid);
+  ::ObjectOperation *oo = (::ObjectOperation *) write_op;
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+
+  ceph::real_time *prt = NULL;
+  ceph::real_time rt;
+
+  if (mtime) {
+    rt = ceph::real_clock::from_time_t(*mtime);
+    prt = &rt;
+  }
+
+  int retval = ctx->operate(obj, oo, prt, translate_flags(flags));
+  tracepoint(librados, rados_write_op_operate_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_write_op_operate2(rados_write_op_t write_op,
+                                       rados_ioctx_t io,
+                                       const char *oid,
+                                       struct timespec *ts,
+                                       int flags)
+{
+  tracepoint(librados, rados_write_op_operate2_enter, write_op, io, oid, ts, flags);
+  object_t obj(oid);
+  ::ObjectOperation *oo = (::ObjectOperation *) write_op;
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+
+  ceph::real_time *prt = NULL;
+  ceph::real_time rt;
+
+  if (ts) {
+    rt = ceph::real_clock::from_timespec(*ts);
+    prt = &rt;
+  }
+
+  int retval = ctx->operate(obj, oo, prt, translate_flags(flags));
+  tracepoint(librados, rados_write_op_operate_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_aio_write_op_operate(rados_write_op_t write_op,
+                                         rados_ioctx_t io,
+                                         rados_completion_t completion,
+                                         const char *oid,
+                                         time_t *mtime,
+                                         int flags)
+{
+  tracepoint(librados, rados_aio_write_op_operate_enter, write_op, io, completion, oid, mtime, flags);
+  object_t obj(oid);
+  ::ObjectOperation *oo = (::ObjectOperation *) write_op;
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  librados::AioCompletionImpl *c = (librados::AioCompletionImpl*)completion;
+  int retval = ctx->aio_operate(obj, oo, c, ctx->snapc, translate_flags(flags));
+  tracepoint(librados, rados_aio_write_op_operate_exit, retval);
+  return retval;
+}
+
+extern "C" rados_read_op_t rados_create_read_op()
+{
+  tracepoint(librados, rados_create_read_op_enter);
+  rados_read_op_t retval = new (std::nothrow)::ObjectOperation;
+  tracepoint(librados, rados_create_read_op_exit, retval);
+  return retval;
+}
+
+extern "C" void rados_release_read_op(rados_read_op_t read_op)
+{
+  tracepoint(librados, rados_release_read_op_enter, read_op);
+  delete (::ObjectOperation *)read_op;
+  tracepoint(librados, rados_release_read_op_exit);
+}
+
+extern "C" void rados_read_op_set_flags(rados_read_op_t read_op, int flags)
+{
+  tracepoint(librados, rados_read_op_set_flags_enter, read_op, flags);
+  ((::ObjectOperation *)read_op)->set_last_op_flags(get_op_flags(flags));
+  tracepoint(librados, rados_read_op_set_flags_exit);
+}
+
+extern "C" void rados_read_op_assert_version(rados_read_op_t read_op, uint64_t ver)
+{
+  tracepoint(librados, rados_read_op_assert_version_enter, read_op, ver);
+  ((::ObjectOperation *)read_op)->assert_version(ver);
+  tracepoint(librados, rados_read_op_assert_version_exit);
+}
+
+extern "C" void rados_read_op_assert_exists(rados_read_op_t read_op)
+{
+  tracepoint(librados, rados_read_op_assert_exists_enter, read_op);
+  ((::ObjectOperation *)read_op)->stat(NULL, (ceph::real_time *)NULL, NULL);
+  tracepoint(librados, rados_read_op_assert_exists_exit);
+}
+
+extern "C" void rados_read_op_cmpext(rados_read_op_t read_op,
+                                    const char *cmp_buf,
+                                    size_t cmp_len,
+                                    uint64_t off,
+                                    int *prval)
+{
+  tracepoint(librados, rados_read_op_cmpext_enter, read_op, cmp_buf,
+            cmp_len, off, prval);
+  ((::ObjectOperation *)read_op)->cmpext(off, cmp_len, cmp_buf, prval);
+  tracepoint(librados, rados_read_op_cmpext_exit);
+}
+
+extern "C" void rados_read_op_cmpxattr(rados_read_op_t read_op,
+                                      const char *name,
+                                      uint8_t comparison_operator,
+                                      const char *value,
+                                      size_t value_len)
+{
+  tracepoint(librados, rados_read_op_cmpxattr_enter, read_op, name, comparison_operator, value, value_len);
+  bufferlist bl;
+  bl.append(value, value_len);
+  ((::ObjectOperation *)read_op)->cmpxattr(name,
+                                          comparison_operator,
+                                          CEPH_OSD_CMPXATTR_MODE_STRING,
+                                          bl);
+  tracepoint(librados, rados_read_op_cmpxattr_exit);
+}
+
+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)
+{
+  tracepoint(librados, rados_read_op_omap_cmp_enter, read_op, key, comparison_operator, val, val_len, prval);
+  rados_c_omap_cmp((::ObjectOperation *)read_op, key, comparison_operator,
+                   val,  strlen(key), val_len, prval);
+  tracepoint(librados, rados_read_op_omap_cmp_exit);
+}
+
+extern "C" void rados_read_op_omap_cmp2(rados_read_op_t read_op,
+                                       const char *key,
+                                       uint8_t comparison_operator,
+                                       const char *val,
+                                       size_t key_len,
+                                       size_t val_len,
+                                       int *prval)
+{
+  tracepoint(librados, rados_read_op_omap_cmp_enter, read_op, key, comparison_operator, val, val_len, prval);
+  rados_c_omap_cmp((::ObjectOperation *)read_op, key, comparison_operator,
+                   val, key_len, val_len, prval);
+  tracepoint(librados, rados_read_op_omap_cmp_exit);
+}
+
+extern "C" void rados_read_op_stat(rados_read_op_t read_op,
+                                  uint64_t *psize,
+                                  time_t *pmtime,
+                                  int *prval)
+{
+  tracepoint(librados, rados_read_op_stat_enter, read_op, psize, pmtime, prval);
+  ((::ObjectOperation *)read_op)->stat(psize, pmtime, prval);
+  tracepoint(librados, rados_read_op_stat_exit);
+}
+
+class C_bl_to_buf : public Context {
+  char *out_buf;
+  size_t out_len;
+  size_t *bytes_read;
+  int *prval;
+public:
+  bufferlist out_bl;
+  C_bl_to_buf(char *out_buf,
+             size_t out_len,
+             size_t *bytes_read,
+             int *prval) : out_buf(out_buf), out_len(out_len),
+                           bytes_read(bytes_read), prval(prval) {}
+  void finish(int r) override {
+    if (out_bl.length() > out_len) {
+      if (prval)
+       *prval = -ERANGE;
+      if (bytes_read)
+       *bytes_read = 0;
+      return;
+    }
+    if (bytes_read)
+      *bytes_read = out_bl.length();
+    if (out_buf && !out_bl.is_provided_buffer(out_buf))
+      out_bl.copy(0, out_bl.length(), out_buf);
+  }
+};
+
+extern "C" void rados_read_op_read(rados_read_op_t read_op,
+                                  uint64_t offset,
+                                  size_t len,
+                                  char *buf,
+                                  size_t *bytes_read,
+                                  int *prval)
+{
+  tracepoint(librados, rados_read_op_read_enter, read_op, offset, len, buf, bytes_read, prval);
+  C_bl_to_buf *ctx = new C_bl_to_buf(buf, len, bytes_read, prval);
+  ctx->out_bl.push_back(buffer::create_static(len, buf));
+  ((::ObjectOperation *)read_op)->read(offset, len, &ctx->out_bl, prval, ctx);
+  tracepoint(librados, rados_read_op_read_exit);
+}
+
+extern "C" void rados_read_op_checksum(rados_read_op_t read_op,
+                                       rados_checksum_type_t type,
+                                       const char *init_value,
+                                       size_t init_value_len,
+                                       uint64_t offset, size_t len,
+                                       size_t chunk_size, char *pchecksum,
+                                      size_t checksum_len, int *prval)
+{
+  tracepoint(librados, rados_read_op_checksum_enter, read_op, type, init_value,
+            init_value_len, offset, len, chunk_size);
+  bufferlist init_value_bl;
+  init_value_bl.append(init_value, init_value_len);
+
+  C_bl_to_buf *ctx = nullptr;
+  if (pchecksum != nullptr) {
+    ctx = new C_bl_to_buf(pchecksum, checksum_len, nullptr, prval);
+  }
+  ((::ObjectOperation *)read_op)->checksum(get_checksum_op_type(type),
+                                          init_value_bl, offset, len,
+                                          chunk_size,
+                                          (ctx ? &ctx->out_bl : nullptr),
+                                          prval, ctx);
+  tracepoint(librados, rados_read_op_checksum_exit);
+}
+
+class C_out_buffer : public Context {
+  char **out_buf;
+  size_t *out_len;
+public:
+  bufferlist out_bl;
+  C_out_buffer(char **out_buf, size_t *out_len) : out_buf(out_buf),
+                                                 out_len(out_len) {}
+  void finish(int r) override {
+    // ignore r since we don't know the meaning of return values
+    // from custom class methods
+    do_out_buffer(out_bl, out_buf, out_len);
+  }
+};
+
+extern "C" void rados_read_op_exec(rados_read_op_t read_op,
+                                  const char *cls,
+                                  const char *method,
+                                  const char *in_buf,
+                                  size_t in_len,
+                                  char **out_buf,
+                                  size_t *out_len,
+                                  int *prval)
+{
+  tracepoint(librados, rados_read_op_exec_enter, read_op, cls, method, in_buf, in_len, out_buf, out_len, prval);
+  bufferlist inbl;
+  inbl.append(in_buf, in_len);
+  C_out_buffer *ctx = new C_out_buffer(out_buf, out_len);
+  ((::ObjectOperation *)read_op)->call(cls, method, inbl, &ctx->out_bl, ctx,
+                                      prval);
+  tracepoint(librados, rados_read_op_exec_exit);
+}
+
+extern "C" void rados_read_op_exec_user_buf(rados_read_op_t read_op,
+                                           const char *cls,
+                                           const char *method,
+                                           const char *in_buf,
+                                           size_t in_len,
+                                           char *out_buf,
+                                           size_t out_len,
+                                           size_t *used_len,
+                                           int *prval)
+{
+  tracepoint(librados, rados_read_op_exec_user_buf_enter, read_op, cls, method, in_buf, in_len, out_buf, out_len, used_len, prval);
+  C_bl_to_buf *ctx = new C_bl_to_buf(out_buf, out_len, used_len, prval);
+  bufferlist inbl;
+  inbl.append(in_buf, in_len);
+  ((::ObjectOperation *)read_op)->call(cls, method, inbl, &ctx->out_bl, ctx,
+                                      prval);
+  tracepoint(librados, rados_read_op_exec_user_buf_exit);
+}
+
+struct RadosOmapIter {
+  std::map<std::string, bufferlist> values;
+  std::map<std::string, bufferlist>::iterator i;
+};
+
+class C_OmapIter : public Context {
+  RadosOmapIter *iter;
+public:
+  explicit C_OmapIter(RadosOmapIter *iter) : iter(iter) {}
+  void finish(int r) override {
+    iter->i = iter->values.begin();
+  }
+};
+
+class C_XattrsIter : public Context {
+  librados::RadosXattrsIter *iter;
+public:
+  explicit C_XattrsIter(librados::RadosXattrsIter *iter) : iter(iter) {}
+  void finish(int r) override {
+    iter->i = iter->attrset.begin();
+  }
+};
+
+extern "C" void rados_read_op_getxattrs(rados_read_op_t read_op,
+                                       rados_xattrs_iter_t *iter,
+                                       int *prval)
+{
+  tracepoint(librados, rados_read_op_getxattrs_enter, read_op, prval);
+  librados::RadosXattrsIter *xattrs_iter = new librados::RadosXattrsIter;
+  ((::ObjectOperation *)read_op)->getxattrs(&xattrs_iter->attrset, prval);
+  ((::ObjectOperation *)read_op)->add_handler(new C_XattrsIter(xattrs_iter));
+  *iter = xattrs_iter;
+  tracepoint(librados, rados_read_op_getxattrs_exit, *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)
+{
+  tracepoint(librados, rados_read_op_omap_get_vals_enter, read_op, start_after, filter_prefix, max_return, 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,
+    nullptr,
+    prval);
+  ((::ObjectOperation *)read_op)->add_handler(new C_OmapIter(omap_iter));
+  *iter = omap_iter;
+  tracepoint(librados, rados_read_op_omap_get_vals_exit, *iter);
+}
+
+extern "C" void rados_read_op_omap_get_vals2(rados_read_op_t read_op,
+                                            const char *start_after,
+                                            const char *filter_prefix,
+                                            uint64_t max_return,
+                                            rados_omap_iter_t *iter,
+                                            unsigned char *pmore,
+                                            int *prval)
+{
+  tracepoint(librados, rados_read_op_omap_get_vals_enter, read_op, start_after, filter_prefix, max_return, 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,
+    (bool*)pmore,
+    prval);
+  ((::ObjectOperation *)read_op)->add_handler(new C_OmapIter(omap_iter));
+  *iter = omap_iter;
+  tracepoint(librados, rados_read_op_omap_get_vals_exit, *iter);
+}
+
+struct C_OmapKeysIter : public Context {
+  RadosOmapIter *iter;
+  std::set<std::string> keys;
+  explicit C_OmapKeysIter(RadosOmapIter *iter) : iter(iter) {}
+  void finish(int r) override {
+    // 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)
+{
+  tracepoint(librados, rados_read_op_omap_get_keys_enter, read_op, start_after, max_return, 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, nullptr, prval);
+  ((::ObjectOperation *)read_op)->add_handler(ctx);
+  *iter = omap_iter;
+  tracepoint(librados, rados_read_op_omap_get_keys_exit, *iter);
+}
+
+extern "C" void rados_read_op_omap_get_keys2(rados_read_op_t read_op,
+                                            const char *start_after,
+                                            uint64_t max_return,
+                                            rados_omap_iter_t *iter,
+                                            unsigned char *pmore,
+                                            int *prval)
+{
+  tracepoint(librados, rados_read_op_omap_get_keys_enter, read_op, start_after, max_return, 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,
+    (bool*)pmore, prval);
+  ((::ObjectOperation *)read_op)->add_handler(ctx);
+  *iter = omap_iter;
+  tracepoint(librados, rados_read_op_omap_get_keys_exit, *iter);
+}
+
+static void internal_rados_read_op_omap_get_vals_by_keys(rados_read_op_t read_op,
+                                                    set<string>& to_get,
+                                                    rados_omap_iter_t *iter,
+                                                    int *prval)
+{
+  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" 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)
+{
+  tracepoint(librados, rados_read_op_omap_get_vals_by_keys_enter, read_op, keys, keys_len, iter, prval);
+  std::set<std::string> to_get(keys, keys + keys_len);
+  internal_rados_read_op_omap_get_vals_by_keys(read_op, to_get, iter, prval);
+  tracepoint(librados, rados_read_op_omap_get_vals_by_keys_exit, *iter);
+}
+
+extern "C" void rados_read_op_omap_get_vals_by_keys2(rados_read_op_t read_op,
+                                                    char const* const* keys,
+                                                    size_t num_keys,
+                                                    const size_t* key_lens,
+                                                    rados_omap_iter_t *iter,
+                                                    int *prval)
+{
+  tracepoint(librados, rados_read_op_omap_get_vals_by_keys_enter, read_op, keys, num_keys, iter, prval);
+  std::set<std::string> to_get;
+  for (size_t i = 0; i < num_keys; i++) {
+    to_get.emplace(keys[i], key_lens[i]);
+  }
+  internal_rados_read_op_omap_get_vals_by_keys(read_op, to_get, iter, prval);
+  tracepoint(librados, rados_read_op_omap_get_vals_by_keys_exit, *iter);
+}
+
+extern "C" int rados_omap_get_next(rados_omap_iter_t iter,
+                                   char **key,
+                                   char **val,
+                                   size_t *len)
+{
+  return rados_omap_get_next2(iter, key, val, nullptr, len);
+}
+
+extern "C" int rados_omap_get_next2(rados_omap_iter_t iter,
+                                   char **key,
+                                   char **val,
+                                   size_t *key_len,
+                                   size_t *val_len)
+{
+  tracepoint(librados, rados_omap_get_next_enter, iter);
+  RadosOmapIter *it = static_cast<RadosOmapIter *>(iter);
+  if (it->i == it->values.end()) {
+    if (key)
+      *key = NULL;
+    if (val)
+      *val = NULL;
+    if (key_len)
+      *key_len = 0;
+    if (val_len)
+      *val_len = 0;
+    tracepoint(librados, rados_omap_get_next_exit, 0, key, val, val_len);
+    return 0;
+  }
+  if (key)
+    *key = (char*)it->i->first.c_str();
+  if (val)
+    *val = it->i->second.c_str();
+  if (key_len)
+    *key_len = it->i->first.length();
+  if (val_len)
+    *val_len = it->i->second.length();
+  ++it->i;
+  tracepoint(librados, rados_omap_get_next_exit, 0, key, val, val_len);
+  return 0;
+}
+
+extern "C" unsigned int rados_omap_iter_size(rados_omap_iter_t iter)
+{
+  RadosOmapIter *it = static_cast<RadosOmapIter *>(iter);
+  return it->values.size();
+}
+
+extern "C" void rados_omap_get_end(rados_omap_iter_t iter)
+{
+  tracepoint(librados, rados_omap_get_end_enter, iter);
+  RadosOmapIter *it = static_cast<RadosOmapIter *>(iter);
+  delete it;
+  tracepoint(librados, rados_omap_get_end_exit);
+}
+
+extern "C" int rados_read_op_operate(rados_read_op_t read_op,
+                                    rados_ioctx_t io,
+                                    const char *oid,
+                                    int flags)
+{
+  tracepoint(librados, rados_read_op_operate_enter, read_op, io, oid, flags);
+  object_t obj(oid);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  int retval = ctx->operate_read(obj, (::ObjectOperation *)read_op, NULL,
+                                translate_flags(flags));
+  tracepoint(librados, rados_read_op_operate_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_aio_read_op_operate(rados_read_op_t read_op,
+                                        rados_ioctx_t io,
+                                        rados_completion_t completion,
+                                        const char *oid,
+                                        int flags)
+{
+  tracepoint(librados, rados_aio_read_op_operate_enter, read_op, io, completion, oid, flags);
+  object_t obj(oid);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  librados::AioCompletionImpl *c = (librados::AioCompletionImpl*)completion;
+  int retval = ctx->aio_operate_read(obj, (::ObjectOperation *)read_op,
+                                    c, translate_flags(flags), NULL);
+  tracepoint(librados, rados_aio_read_op_operate_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_cache_pin(rados_ioctx_t io, const char *o)
+{
+  tracepoint(librados, rados_cache_pin_enter, io, o);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  object_t oid(o);
+  int retval = ctx->cache_pin(oid);
+  tracepoint(librados, rados_cache_pin_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_cache_unpin(rados_ioctx_t io, const char *o)
+{
+  tracepoint(librados, rados_cache_unpin_enter, io, o);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  object_t oid(o);
+  int retval = ctx->cache_unpin(oid);
+  tracepoint(librados, rados_cache_unpin_exit, retval);
+  return retval;
+}
+
+CEPH_RADOS_API void rados_object_list_slice(
+    rados_ioctx_t io,
+    const rados_object_list_cursor start,
+    const rados_object_list_cursor finish,
+    const size_t n,
+    const size_t m,
+    rados_object_list_cursor *split_start,
+    rados_object_list_cursor *split_finish)
+{
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+
+  ceph_assert(split_start);
+  ceph_assert(split_finish);
+  hobject_t *split_start_hobj = (hobject_t*)(*split_start);
+  hobject_t *split_finish_hobj = (hobject_t*)(*split_finish);
+  ceph_assert(split_start_hobj);
+  ceph_assert(split_finish_hobj);
+  hobject_t *start_hobj = (hobject_t*)(start);
+  hobject_t *finish_hobj = (hobject_t*)(finish);
+
+  ctx->object_list_slice(
+      *start_hobj,
+      *finish_hobj,
+      n,
+      m,
+      split_start_hobj,
+      split_finish_hobj);
+}
diff --git a/src/librados/librados_cxx.cc b/src/librados/librados_cxx.cc
new file mode 100644 (file)
index 0000000..c1d2f3f
--- /dev/null
@@ -0,0 +1,2987 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2004-2012 Sage Weil <sage@newdream.net>
+ *
+ * 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.
+ *
+ */
+
+#include <limits.h>
+
+#include "common/config.h"
+#include "common/errno.h"
+#include "common/ceph_argparse.h"
+#include "common/ceph_json.h"
+#include "common/common_init.h"
+#include "common/TracepointProvider.h"
+#include "common/hobject.h"
+#include "include/rados/librados.h"
+#include "include/rados/librados.hpp"
+#include "include/types.h"
+#include <include/stringify.h>
+
+#include "librados/AioCompletionImpl.h"
+#include "librados/IoCtxImpl.h"
+#include "librados/PoolAsyncCompletionImpl.h"
+#include "librados/RadosClient.h"
+#include "librados/RadosXattrIter.h"
+#include "librados/ListObjectImpl.h"
+#include "librados/librados_util.h"
+#include "cls/lock/cls_lock_client.h"
+
+#include <string>
+#include <map>
+#include <set>
+#include <vector>
+#include <list>
+#include <stdexcept>
+
+#ifdef WITH_LTTNG
+#define TRACEPOINT_DEFINE
+#define TRACEPOINT_PROBE_DYNAMIC_LINKAGE
+#include "tracing/librados.h"
+#undef TRACEPOINT_PROBE_DYNAMIC_LINKAGE
+#undef TRACEPOINT_DEFINE
+#else
+#define tracepoint(...)
+#endif
+
+using std::string;
+using std::map;
+using std::set;
+using std::vector;
+using std::list;
+using std::runtime_error;
+
+#define dout_subsys ceph_subsys_rados
+#undef dout_prefix
+#define dout_prefix *_dout << "librados: "
+
+static TracepointProvider::Traits tracepoint_traits("librados_tp.so", "rados_tracing");
+
+/*
+ * Structure of this file
+ *
+ * RadosClient and the related classes are the internal implementation of librados.
+ * Above that layer sits the C API, found in include/rados/librados.h, and
+ * the C++ API, found in include/rados/librados.hpp
+ *
+ * The C++ API sometimes implements things in terms of the C API.
+ * Both the C++ and C API rely on RadosClient.
+ *
+ * Visually:
+ * +--------------------------------------+
+ * |             C++ API                  |
+ * +--------------------+                 |
+ * |       C API        |                 |
+ * +--------------------+-----------------+
+ * |          RadosClient                 |
+ * +--------------------------------------+
+ */
+
+namespace librados {
+
+struct ObjectOperationImpl {
+  ::ObjectOperation o;
+  real_time rt;
+  real_time *prt;
+
+  ObjectOperationImpl() : prt(NULL) {}
+};
+
+}
+
+size_t librados::ObjectOperation::size()
+{
+  ::ObjectOperation *o = &impl->o;
+  return o->size();
+}
+
+//deprcated
+void librados::ObjectOperation::set_op_flags(ObjectOperationFlags flags)
+{
+  set_op_flags2((int)flags);
+}
+
+void librados::ObjectOperation::set_op_flags2(int flags)
+{
+  impl->o.set_last_op_flags(get_op_flags(flags));
+}
+
+void librados::ObjectOperation::cmpext(uint64_t off,
+                                       bufferlist &cmp_bl,
+                                       int *prval)
+{
+  ::ObjectOperation *o = &impl->o;
+  o->cmpext(off, cmp_bl, prval);
+}
+
+void librados::ObjectOperation::cmpxattr(const char *name, uint8_t op, const bufferlist& v)
+{
+  ::ObjectOperation *o = &impl->o;
+  o->cmpxattr(name, op, CEPH_OSD_CMPXATTR_MODE_STRING, v);
+}
+
+void librados::ObjectOperation::cmpxattr(const char *name, uint8_t op, uint64_t v)
+{
+  ::ObjectOperation *o = &impl->o;
+  bufferlist bl;
+  encode(v, bl);
+  o->cmpxattr(name, op, CEPH_OSD_CMPXATTR_MODE_U64, bl);
+}
+
+void librados::ObjectOperation::assert_version(uint64_t ver)
+{
+  ::ObjectOperation *o = &impl->o;
+  o->assert_version(ver);
+}
+
+void librados::ObjectOperation::assert_exists()
+{
+  ::ObjectOperation *o = &impl->o;
+  o->stat(NULL, (ceph::real_time*) NULL, NULL);
+}
+
+void librados::ObjectOperation::exec(const char *cls, const char *method, bufferlist& inbl)
+{
+  ::ObjectOperation *o = &impl->o;
+  o->call(cls, method, inbl);
+}
+
+void librados::ObjectOperation::exec(const char *cls, const char *method, bufferlist& inbl, bufferlist *outbl, int *prval)
+{
+  ::ObjectOperation *o = &impl->o;
+  o->call(cls, method, inbl, outbl, NULL, prval);
+}
+
+class ObjectOpCompletionCtx : public Context {
+  librados::ObjectOperationCompletion *completion;
+  bufferlist bl;
+public:
+  explicit ObjectOpCompletionCtx(librados::ObjectOperationCompletion *c) : completion(c) {}
+  void finish(int r) override {
+    completion->handle_completion(r, bl);
+    delete completion;
+  }
+
+  bufferlist *outbl() {
+    return &bl;
+  }
+};
+
+void librados::ObjectOperation::exec(const char *cls, const char *method, bufferlist& inbl, librados::ObjectOperationCompletion *completion)
+{
+  ::ObjectOperation *o = &impl->o;
+
+  ObjectOpCompletionCtx *ctx = new ObjectOpCompletionCtx(completion);
+
+  o->call(cls, method, inbl, ctx->outbl(), ctx, NULL);
+}
+
+void librados::ObjectReadOperation::stat(uint64_t *psize, time_t *pmtime, int *prval)
+{
+  ::ObjectOperation *o = &impl->o;
+  o->stat(psize, pmtime, prval);
+}
+
+void librados::ObjectReadOperation::stat2(uint64_t *psize, struct timespec *pts, int *prval)
+{
+  ::ObjectOperation *o = &impl->o;
+  o->stat(psize, pts, prval);
+}
+
+void librados::ObjectReadOperation::read(size_t off, uint64_t len, bufferlist *pbl, int *prval)
+{
+  ::ObjectOperation *o = &impl->o;
+  o->read(off, len, pbl, prval, NULL);
+}
+
+void librados::ObjectReadOperation::sparse_read(uint64_t off, uint64_t len,
+                                               std::map<uint64_t,uint64_t> *m,
+                                               bufferlist *data_bl, int *prval)
+{
+  ::ObjectOperation *o = &impl->o;
+  o->sparse_read(off, len, m, data_bl, prval);
+}
+
+void librados::ObjectReadOperation::checksum(rados_checksum_type_t type,
+                                            const bufferlist &init_value_bl,
+                                            uint64_t off, size_t len,
+                                            size_t chunk_size, bufferlist *pbl,
+                                            int *prval)
+{
+  ::ObjectOperation *o = &impl->o;
+  o->checksum(get_checksum_op_type(type), init_value_bl, off, len, chunk_size,
+             pbl, prval, nullptr);
+}
+
+void librados::ObjectReadOperation::tmap_get(bufferlist *pbl, int *prval)
+{
+  ::ObjectOperation *o = &impl->o;
+  o->tmap_get(pbl, prval);
+}
+
+void librados::ObjectReadOperation::getxattr(const char *name, bufferlist *pbl, int *prval)
+{
+  ::ObjectOperation *o = &impl->o;
+  o->getxattr(name, pbl, prval);
+}
+
+void librados::ObjectReadOperation::omap_get_vals(
+  const std::string &start_after,
+  const std::string &filter_prefix,
+  uint64_t max_return,
+  std::map<std::string, bufferlist> *out_vals,
+  int *prval)
+{
+  ::ObjectOperation *o = &impl->o;
+  o->omap_get_vals(start_after, filter_prefix, max_return, out_vals, nullptr,
+                  prval);
+}
+
+void librados::ObjectReadOperation::omap_get_vals2(
+  const std::string &start_after,
+  const std::string &filter_prefix,
+  uint64_t max_return,
+  std::map<std::string, bufferlist> *out_vals,
+  bool *pmore,
+  int *prval)
+{
+  ::ObjectOperation *o = &impl->o;
+  o->omap_get_vals(start_after, filter_prefix, max_return, out_vals, pmore,
+                  prval);
+}
+
+void librados::ObjectReadOperation::omap_get_vals(
+  const std::string &start_after,
+  uint64_t max_return,
+  std::map<std::string, bufferlist> *out_vals,
+  int *prval)
+{
+  ::ObjectOperation *o = &impl->o;
+  o->omap_get_vals(start_after, "", max_return, out_vals, nullptr, prval);
+}
+
+void librados::ObjectReadOperation::omap_get_vals2(
+  const std::string &start_after,
+  uint64_t max_return,
+  std::map<std::string, bufferlist> *out_vals,
+  bool *pmore,
+  int *prval)
+{
+  ::ObjectOperation *o = &impl->o;
+  o->omap_get_vals(start_after, "", max_return, out_vals, pmore, prval);
+}
+
+void librados::ObjectReadOperation::omap_get_keys(
+  const std::string &start_after,
+  uint64_t max_return,
+  std::set<std::string> *out_keys,
+  int *prval)
+{
+  ::ObjectOperation *o = &impl->o;
+  o->omap_get_keys(start_after, max_return, out_keys, nullptr, prval);
+}
+
+void librados::ObjectReadOperation::omap_get_keys2(
+  const std::string &start_after,
+  uint64_t max_return,
+  std::set<std::string> *out_keys,
+  bool *pmore,
+  int *prval)
+{
+  ::ObjectOperation *o = &impl->o;
+  o->omap_get_keys(start_after, max_return, out_keys, pmore, prval);
+}
+
+void librados::ObjectReadOperation::omap_get_header(bufferlist *bl, int *prval)
+{
+  ::ObjectOperation *o = &impl->o;
+  o->omap_get_header(bl, prval);
+}
+
+void librados::ObjectReadOperation::omap_get_vals_by_keys(
+  const std::set<std::string> &keys,
+  std::map<std::string, bufferlist> *map,
+  int *prval)
+{
+  ::ObjectOperation *o = &impl->o;
+  o->omap_get_vals_by_keys(keys, map, prval);
+}
+
+void librados::ObjectOperation::omap_cmp(
+  const std::map<std::string, pair<bufferlist, int> > &assertions,
+  int *prval)
+{
+  ::ObjectOperation *o = &impl->o;
+  o->omap_cmp(assertions, prval);
+}
+
+void librados::ObjectReadOperation::list_watchers(
+  list<obj_watch_t> *out_watchers,
+  int *prval)
+{
+  ::ObjectOperation *o = &impl->o;
+  o->list_watchers(out_watchers, prval);
+}
+
+void librados::ObjectReadOperation::list_snaps(
+  snap_set_t *out_snaps,
+  int *prval)
+{
+  ::ObjectOperation *o = &impl->o;
+  o->list_snaps(out_snaps, prval);
+}
+
+void librados::ObjectReadOperation::is_dirty(bool *is_dirty, int *prval)
+{
+  ::ObjectOperation *o = &impl->o;
+  o->is_dirty(is_dirty, prval);
+}
+
+int librados::IoCtx::omap_get_vals(const std::string& oid,
+                                   const std::string& orig_start_after,
+                                   const std::string& filter_prefix,
+                                   uint64_t max_return,
+                                   std::map<std::string, bufferlist> *out_vals)
+{
+  bool first = true;
+  string start_after = orig_start_after;
+  bool more = true;
+  while (max_return > 0 && more) {
+    std::map<std::string,bufferlist> out;
+    ObjectReadOperation op;
+    op.omap_get_vals2(start_after, filter_prefix, max_return, &out, &more,
+                     nullptr);
+    bufferlist bl;
+    int ret = operate(oid, &op, &bl);
+    if (ret < 0) {
+      return ret;
+    }
+    if (more) {
+      if (out.empty()) {
+       return -EINVAL;  // wth
+      }
+      start_after = out.rbegin()->first;
+    }
+    if (out.size() <= max_return) {
+      max_return -= out.size();
+    } else {
+      max_return = 0;
+    }
+    if (first) {
+      out_vals->swap(out);
+      first = false;
+    } else {
+      out_vals->insert(out.begin(), out.end());
+      out.clear();
+    }
+  }
+  return 0;
+}
+
+int librados::IoCtx::omap_get_vals2(
+  const std::string& oid,
+  const std::string& start_after,
+  const std::string& filter_prefix,
+  uint64_t max_return,
+  std::map<std::string, bufferlist> *out_vals,
+  bool *pmore)
+{
+  ObjectReadOperation op;
+  int r;
+  op.omap_get_vals2(start_after, filter_prefix, max_return, out_vals, pmore, &r);
+  bufferlist bl;
+  int ret = operate(oid, &op, &bl);
+  if (ret < 0)
+    return ret;
+  return r;
+}
+
+void librados::ObjectReadOperation::getxattrs(map<string, bufferlist> *pattrs, int *prval)
+{
+  ::ObjectOperation *o = &impl->o;
+  o->getxattrs(pattrs, prval);
+}
+
+void librados::ObjectWriteOperation::mtime(time_t *pt)
+{
+  if (pt) {
+    impl->rt = ceph::real_clock::from_time_t(*pt);
+    impl->prt = &impl->rt;
+  }
+}
+
+void librados::ObjectWriteOperation::mtime2(struct timespec *pts)
+{
+  if (pts) {
+    impl->rt = ceph::real_clock::from_timespec(*pts);
+    impl->prt = &impl->rt;
+  }
+}
+
+void librados::ObjectWriteOperation::create(bool exclusive)
+{
+  ::ObjectOperation *o = &impl->o;
+  o->create(exclusive);
+}
+
+void librados::ObjectWriteOperation::create(bool exclusive,
+                                           const std::string& category) // unused
+{
+  ::ObjectOperation *o = &impl->o;
+  o->create(exclusive);
+}
+
+void librados::ObjectWriteOperation::write(uint64_t off, const bufferlist& bl)
+{
+  ::ObjectOperation *o = &impl->o;
+  bufferlist c = bl;
+  o->write(off, c);
+}
+
+void librados::ObjectWriteOperation::write_full(const bufferlist& bl)
+{
+  ::ObjectOperation *o = &impl->o;
+  bufferlist c = bl;
+  o->write_full(c);
+}
+
+void librados::ObjectWriteOperation::writesame(uint64_t off, uint64_t write_len,
+                                              const bufferlist& bl)
+{
+  ::ObjectOperation *o = &impl->o;
+  bufferlist c = bl;
+  o->writesame(off, write_len, c);
+}
+
+void librados::ObjectWriteOperation::append(const bufferlist& bl)
+{
+  ::ObjectOperation *o = &impl->o;
+  bufferlist c = bl;
+  o->append(c);
+}
+
+void librados::ObjectWriteOperation::remove()
+{
+  ::ObjectOperation *o = &impl->o;
+  o->remove();
+}
+
+void librados::ObjectWriteOperation::truncate(uint64_t off)
+{
+  ::ObjectOperation *o = &impl->o;
+  o->truncate(off);
+}
+
+void librados::ObjectWriteOperation::zero(uint64_t off, uint64_t len)
+{
+  ::ObjectOperation *o = &impl->o;
+  o->zero(off, len);
+}
+
+void librados::ObjectWriteOperation::rmxattr(const char *name)
+{
+  ::ObjectOperation *o = &impl->o;
+  o->rmxattr(name);
+}
+
+void librados::ObjectWriteOperation::setxattr(const char *name, const bufferlist& v)
+{
+  ::ObjectOperation *o = &impl->o;
+  o->setxattr(name, v);
+}
+
+void librados::ObjectWriteOperation::omap_set(
+  const map<string, bufferlist> &map)
+{
+  ::ObjectOperation *o = &impl->o;
+  o->omap_set(map);
+}
+
+void librados::ObjectWriteOperation::omap_set_header(const bufferlist &bl)
+{
+  bufferlist c = bl;
+  ::ObjectOperation *o = &impl->o;
+  o->omap_set_header(c);
+}
+
+void librados::ObjectWriteOperation::omap_clear()
+{
+  ::ObjectOperation *o = &impl->o;
+  o->omap_clear();
+}
+
+void librados::ObjectWriteOperation::omap_rm_keys(
+  const std::set<std::string> &to_rm)
+{
+  ::ObjectOperation *o = &impl->o;
+  o->omap_rm_keys(to_rm);
+}
+
+void librados::ObjectWriteOperation::copy_from(const std::string& src,
+                                               const IoCtx& src_ioctx,
+                                               uint64_t src_version)
+{
+  copy_from2(src, src_ioctx, src_version, 0);
+}
+
+void librados::ObjectWriteOperation::copy_from2(const std::string& src,
+                                               const IoCtx& src_ioctx,
+                                               uint64_t src_version,
+                                               uint32_t src_fadvise_flags)
+{
+  ::ObjectOperation *o = &impl->o;
+  o->copy_from(object_t(src), src_ioctx.io_ctx_impl->snap_seq,
+              src_ioctx.io_ctx_impl->oloc, src_version, 0, src_fadvise_flags);
+}
+
+void librados::ObjectWriteOperation::undirty()
+{
+  ::ObjectOperation *o = &impl->o;
+  o->undirty();
+}
+
+void librados::ObjectReadOperation::cache_flush()
+{
+  ::ObjectOperation *o = &impl->o;
+  o->cache_flush();
+}
+
+void librados::ObjectReadOperation::cache_try_flush()
+{
+  ::ObjectOperation *o = &impl->o;
+  o->cache_try_flush();
+}
+
+void librados::ObjectReadOperation::cache_evict()
+{
+  ::ObjectOperation *o = &impl->o;
+  o->cache_evict();
+}
+
+void librados::ObjectWriteOperation::set_redirect(const std::string& tgt_obj, 
+                                                 const IoCtx& tgt_ioctx,
+                                                 uint64_t tgt_version,
+                                                 int flag)
+{
+  ::ObjectOperation *o = &impl->o;
+  o->set_redirect(object_t(tgt_obj), tgt_ioctx.io_ctx_impl->snap_seq,
+                         tgt_ioctx.io_ctx_impl->oloc, tgt_version, flag);
+}
+
+void librados::ObjectWriteOperation::set_chunk(uint64_t src_offset,
+                                              uint64_t src_length,
+                                              const IoCtx& tgt_ioctx,
+                                              string tgt_oid,
+                                              uint64_t tgt_offset,
+                                              int flag)
+{
+  ::ObjectOperation *o = &impl->o;
+  o->set_chunk(src_offset, src_length, 
+              tgt_ioctx.io_ctx_impl->oloc, object_t(tgt_oid), tgt_offset, flag);
+}
+
+void librados::ObjectWriteOperation::tier_promote()
+{
+  ::ObjectOperation *o = &impl->o;
+  o->tier_promote();
+}
+
+void librados::ObjectWriteOperation::unset_manifest()
+{
+  ::ObjectOperation *o = &impl->o;
+  o->unset_manifest();
+}
+
+void librados::ObjectWriteOperation::tmap_put(const bufferlist &bl)
+{
+  ::ObjectOperation *o = &impl->o;
+  bufferlist c = bl;
+  o->tmap_put(c);
+}
+
+void librados::ObjectWriteOperation::tmap_update(const bufferlist& cmdbl)
+{
+  ::ObjectOperation *o = &impl->o;
+  bufferlist c = cmdbl;
+  o->tmap_update(c);
+}
+
+void librados::ObjectWriteOperation::selfmanaged_snap_rollback(snap_t snapid)
+{
+  ::ObjectOperation *o = &impl->o;
+  o->rollback(snapid);
+}
+
+// You must specify the snapid not the name normally used with pool snapshots
+void librados::ObjectWriteOperation::snap_rollback(snap_t snapid)
+{
+  ::ObjectOperation *o = &impl->o;
+  o->rollback(snapid);
+}
+
+void librados::ObjectWriteOperation::set_alloc_hint(
+                                            uint64_t expected_object_size,
+                                            uint64_t expected_write_size)
+{
+  ::ObjectOperation *o = &impl->o;
+  o->set_alloc_hint(expected_object_size, expected_write_size, 0);
+}
+void librados::ObjectWriteOperation::set_alloc_hint2(
+                                            uint64_t expected_object_size,
+                                            uint64_t expected_write_size,
+                                           uint32_t flags)
+{
+  ::ObjectOperation *o = &impl->o;
+  o->set_alloc_hint(expected_object_size, expected_write_size, flags);
+}
+
+void librados::ObjectWriteOperation::cache_pin()
+{
+  ::ObjectOperation *o = &impl->o;
+  o->cache_pin();
+}
+
+void librados::ObjectWriteOperation::cache_unpin()
+{
+  ::ObjectOperation *o = &impl->o;
+  o->cache_unpin();
+}
+
+librados::WatchCtx::
+~WatchCtx()
+{
+}
+
+librados::WatchCtx2::
+~WatchCtx2()
+{
+}
+
+///////////////////////////// NObjectIteratorImpl /////////////////////////////
+librados::NObjectIteratorImpl::NObjectIteratorImpl(ObjListCtx *ctx_)
+  : ctx(ctx_)
+{
+}
+
+librados::NObjectIteratorImpl::~NObjectIteratorImpl()
+{
+  ctx.reset();
+}
+
+librados::NObjectIteratorImpl::NObjectIteratorImpl(const NObjectIteratorImpl &rhs)
+{
+  *this = rhs;
+}
+
+librados::NObjectIteratorImpl& librados::NObjectIteratorImpl::operator=(const librados::NObjectIteratorImpl &rhs)
+{
+  if (&rhs == this)
+    return *this;
+  if (rhs.ctx.get() == NULL) {
+    ctx.reset();
+    return *this;
+  }
+  Objecter::NListContext *list_ctx = new Objecter::NListContext(*rhs.ctx->nlc);
+  ctx.reset(new ObjListCtx(rhs.ctx->ctx, list_ctx));
+  cur_obj = rhs.cur_obj;
+  return *this;
+}
+
+bool librados::NObjectIteratorImpl::operator==(const librados::NObjectIteratorImpl& rhs) const {
+
+  if (ctx.get() == NULL) {
+    if (rhs.ctx.get() == NULL)
+      return true;
+    return rhs.ctx->nlc->at_end();
+  }
+  if (rhs.ctx.get() == NULL) {
+    // Redundant but same as ObjectIterator version
+    if (ctx.get() == NULL)
+      return true;
+    return ctx->nlc->at_end();
+  }
+  return ctx.get() == rhs.ctx.get();
+}
+
+bool librados::NObjectIteratorImpl::operator!=(const librados::NObjectIteratorImpl& rhs) const {
+  return !(*this == rhs);
+}
+
+const librados::ListObject& librados::NObjectIteratorImpl::operator*() const {
+  return cur_obj;
+}
+
+const librados::ListObject* librados::NObjectIteratorImpl::operator->() const {
+  return &cur_obj;
+}
+
+librados::NObjectIteratorImpl& librados::NObjectIteratorImpl::operator++()
+{
+  get_next();
+  return *this;
+}
+
+librados::NObjectIteratorImpl librados::NObjectIteratorImpl::operator++(int)
+{
+  librados::NObjectIteratorImpl ret(*this);
+  get_next();
+  return ret;
+}
+
+uint32_t librados::NObjectIteratorImpl::seek(uint32_t pos)
+{
+  uint32_t r = rados_nobjects_list_seek(ctx.get(), pos);
+  get_next();
+  return r;
+}
+
+uint32_t librados::NObjectIteratorImpl::seek(const ObjectCursor& cursor)
+{
+  uint32_t r = rados_nobjects_list_seek_cursor(ctx.get(), (rados_object_list_cursor)cursor.c_cursor);
+  get_next();
+  return r;
+}
+
+librados::ObjectCursor librados::NObjectIteratorImpl::get_cursor()
+{
+  librados::ObjListCtx *lh = (librados::ObjListCtx *)ctx.get();
+  librados::ObjectCursor oc;
+  oc.set(lh->ctx->nlist_get_cursor(lh->nlc));
+  return oc;
+}
+
+void librados::NObjectIteratorImpl::set_filter(const bufferlist &bl)
+{
+  ceph_assert(ctx);
+  ctx->nlc->filter = bl;
+}
+
+void librados::NObjectIteratorImpl::get_next()
+{
+  const char *entry, *key, *nspace;
+  if (ctx->nlc->at_end())
+    return;
+  int ret = rados_nobjects_list_next(ctx.get(), &entry, &key, &nspace);
+  if (ret == -ENOENT) {
+    return;
+  }
+  else if (ret) {
+    ostringstream oss;
+    oss << "rados returned " << cpp_strerror(ret);
+    throw std::runtime_error(oss.str());
+  }
+
+  if (cur_obj.impl == NULL)
+    cur_obj.impl = new ListObjectImpl();
+  cur_obj.impl->nspace = nspace;
+  cur_obj.impl->oid = entry;
+  cur_obj.impl->locator = key ? key : string();
+}
+
+uint32_t librados::NObjectIteratorImpl::get_pg_hash_position() const
+{
+  return ctx->nlc->get_pg_hash_position();
+}
+
+///////////////////////////// NObjectIterator /////////////////////////////
+librados::NObjectIterator::NObjectIterator(ObjListCtx *ctx_)
+{
+  impl = new NObjectIteratorImpl(ctx_);
+}
+
+librados::NObjectIterator::~NObjectIterator()
+{
+  delete impl;
+}
+
+librados::NObjectIterator::NObjectIterator(const NObjectIterator &rhs)
+{
+  if (rhs.impl == NULL) {
+    impl = NULL;
+    return;
+  }
+  impl = new NObjectIteratorImpl();
+  *impl = *(rhs.impl);
+}
+
+librados::NObjectIterator& librados::NObjectIterator::operator=(const librados::NObjectIterator &rhs)
+{
+  if (rhs.impl == NULL) {
+    delete impl;
+    impl = NULL;
+    return *this;
+  }
+  if (impl == NULL)
+    impl = new NObjectIteratorImpl();
+  *impl = *(rhs.impl);
+  return *this;
+}
+
+bool librados::NObjectIterator::operator==(const librados::NObjectIterator& rhs) const 
+{
+  if (impl && rhs.impl) {
+    return *impl == *(rhs.impl);
+  } else {
+    return impl == rhs.impl;
+  }
+}
+
+bool librados::NObjectIterator::operator!=(const librados::NObjectIterator& rhs) const
+{
+  return !(*this == rhs);
+}
+
+const librados::ListObject& librados::NObjectIterator::operator*() const {
+  ceph_assert(impl);
+  return *(impl->get_listobjectp());
+}
+
+const librados::ListObject* librados::NObjectIterator::operator->() const {
+  ceph_assert(impl);
+  return impl->get_listobjectp();
+}
+
+librados::NObjectIterator& librados::NObjectIterator::operator++()
+{
+  ceph_assert(impl);
+  impl->get_next();
+  return *this;
+}
+
+librados::NObjectIterator librados::NObjectIterator::operator++(int)
+{
+  librados::NObjectIterator ret(*this);
+  impl->get_next();
+  return ret;
+}
+
+uint32_t librados::NObjectIterator::seek(uint32_t pos)
+{
+  ceph_assert(impl);
+  return impl->seek(pos);
+}
+
+uint32_t librados::NObjectIterator::seek(const ObjectCursor& cursor)
+{
+  ceph_assert(impl);
+  return impl->seek(cursor);
+}
+
+librados::ObjectCursor librados::NObjectIterator::get_cursor()
+{
+  ceph_assert(impl);
+  return impl->get_cursor();
+}
+
+void librados::NObjectIterator::set_filter(const bufferlist &bl)
+{
+  impl->set_filter(bl);
+}
+
+void librados::NObjectIterator::get_next()
+{
+  ceph_assert(impl);
+  impl->get_next();
+}
+
+uint32_t librados::NObjectIterator::get_pg_hash_position() const
+{
+  ceph_assert(impl);
+  return impl->get_pg_hash_position();
+}
+
+const librados::NObjectIterator librados::NObjectIterator::__EndObjectIterator(NULL);
+
+///////////////////////////// PoolAsyncCompletion //////////////////////////////
+int librados::PoolAsyncCompletion::PoolAsyncCompletion::set_callback(void *cb_arg,
+                                                                    rados_callback_t cb)
+{
+  PoolAsyncCompletionImpl *c = (PoolAsyncCompletionImpl *)pc;
+  return c->set_callback(cb_arg, cb);
+}
+
+int librados::PoolAsyncCompletion::PoolAsyncCompletion::wait()
+{
+  PoolAsyncCompletionImpl *c = (PoolAsyncCompletionImpl *)pc;
+  return c->wait();
+}
+
+bool librados::PoolAsyncCompletion::PoolAsyncCompletion::is_complete()
+{
+  PoolAsyncCompletionImpl *c = (PoolAsyncCompletionImpl *)pc;
+  return c->is_complete();
+}
+
+int librados::PoolAsyncCompletion::PoolAsyncCompletion::get_return_value()
+{
+  PoolAsyncCompletionImpl *c = (PoolAsyncCompletionImpl *)pc;
+  return c->get_return_value();
+}
+
+void librados::PoolAsyncCompletion::PoolAsyncCompletion::release()
+{
+  PoolAsyncCompletionImpl *c = (PoolAsyncCompletionImpl *)pc;
+  c->release();
+  delete this;
+}
+
+///////////////////////////// AioCompletion //////////////////////////////
+int librados::AioCompletion::AioCompletion::set_complete_callback(void *cb_arg, rados_callback_t cb)
+{
+  AioCompletionImpl *c = (AioCompletionImpl *)pc;
+  return c->set_complete_callback(cb_arg, cb);
+}
+
+int librados::AioCompletion::AioCompletion::set_safe_callback(void *cb_arg, rados_callback_t cb)
+{
+  AioCompletionImpl *c = (AioCompletionImpl *)pc;
+  return c->set_safe_callback(cb_arg, cb);
+}
+
+int librados::AioCompletion::AioCompletion::wait_for_complete()
+{
+  AioCompletionImpl *c = (AioCompletionImpl *)pc;
+  return c->wait_for_complete();
+}
+
+int librados::AioCompletion::AioCompletion::wait_for_safe()
+{
+  AioCompletionImpl *c = (AioCompletionImpl *)pc;
+  return c->wait_for_safe();
+}
+
+bool librados::AioCompletion::AioCompletion::is_complete()
+{
+  AioCompletionImpl *c = (AioCompletionImpl *)pc;
+  return c->is_complete();
+}
+
+bool librados::AioCompletion::AioCompletion::is_safe()
+{
+  AioCompletionImpl *c = (AioCompletionImpl *)pc;
+  return c->is_safe();
+}
+
+int librados::AioCompletion::AioCompletion::wait_for_complete_and_cb()
+{
+  AioCompletionImpl *c = (AioCompletionImpl *)pc;
+  return c->wait_for_complete_and_cb();
+}
+
+int librados::AioCompletion::AioCompletion::wait_for_safe_and_cb()
+{
+  AioCompletionImpl *c = (AioCompletionImpl *)pc;
+  return c->wait_for_safe_and_cb();
+}
+
+bool librados::AioCompletion::AioCompletion::is_complete_and_cb()
+{
+  AioCompletionImpl *c = (AioCompletionImpl *)pc;
+  return c->is_complete_and_cb();
+}
+
+bool librados::AioCompletion::AioCompletion::is_safe_and_cb()
+{
+  AioCompletionImpl *c = (AioCompletionImpl *)pc;
+  return c->is_safe_and_cb();
+}
+
+int librados::AioCompletion::AioCompletion::get_return_value()
+{
+  AioCompletionImpl *c = (AioCompletionImpl *)pc;
+  return c->get_return_value();
+}
+
+int librados::AioCompletion::AioCompletion::get_version()
+{
+  AioCompletionImpl *c = (AioCompletionImpl *)pc;
+  return c->get_version();
+}
+
+uint64_t librados::AioCompletion::AioCompletion::get_version64()
+{
+  AioCompletionImpl *c = (AioCompletionImpl *)pc;
+  return c->get_version();
+}
+
+void librados::AioCompletion::AioCompletion::release()
+{
+  AioCompletionImpl *c = (AioCompletionImpl *)pc;
+  c->release();
+  delete this;
+}
+
+///////////////////////////// IoCtx //////////////////////////////
+librados::IoCtx::IoCtx() : io_ctx_impl(NULL)
+{
+}
+
+void librados::IoCtx::from_rados_ioctx_t(rados_ioctx_t p, IoCtx &io)
+{
+  IoCtxImpl *io_ctx_impl = (IoCtxImpl*)p;
+
+  io.io_ctx_impl = io_ctx_impl;
+  if (io_ctx_impl) {
+    io_ctx_impl->get();
+  }
+}
+
+librados::IoCtx::IoCtx(const IoCtx& rhs)
+{
+  io_ctx_impl = rhs.io_ctx_impl;
+  if (io_ctx_impl) {
+    io_ctx_impl->get();
+  }
+}
+
+librados::IoCtx& librados::IoCtx::operator=(const IoCtx& rhs)
+{
+  if (io_ctx_impl)
+    io_ctx_impl->put();
+  io_ctx_impl = rhs.io_ctx_impl;
+  io_ctx_impl->get();
+  return *this;
+}
+
+librados::IoCtx::~IoCtx()
+{
+  close();
+}
+
+void librados::IoCtx::close()
+{
+  if (io_ctx_impl)
+    io_ctx_impl->put();
+  io_ctx_impl = 0;
+}
+
+void librados::IoCtx::dup(const IoCtx& rhs)
+{
+  if (io_ctx_impl)
+    io_ctx_impl->put();
+  io_ctx_impl = new IoCtxImpl();
+  io_ctx_impl->get();
+  io_ctx_impl->dup(*rhs.io_ctx_impl);
+}
+
+int librados::IoCtx::set_auid(uint64_t auid_)
+{
+  return -EOPNOTSUPP;
+}
+
+int librados::IoCtx::set_auid_async(uint64_t auid_, PoolAsyncCompletion *c)
+{
+  return -EOPNOTSUPP;
+}
+
+int librados::IoCtx::get_auid(uint64_t *auid_)
+{
+  return -EOPNOTSUPP;
+}
+
+bool librados::IoCtx::pool_requires_alignment()
+{
+  return io_ctx_impl->client->pool_requires_alignment(get_id());
+}
+
+int librados::IoCtx::pool_requires_alignment2(bool *requires)
+{
+  return io_ctx_impl->client->pool_requires_alignment2(get_id(), requires);
+}
+
+uint64_t librados::IoCtx::pool_required_alignment()
+{
+  return io_ctx_impl->client->pool_required_alignment(get_id());
+}
+
+int librados::IoCtx::pool_required_alignment2(uint64_t *alignment)
+{
+  return io_ctx_impl->client->pool_required_alignment2(get_id(), alignment);
+}
+
+std::string librados::IoCtx::get_pool_name()
+{
+  std::string s;
+  io_ctx_impl->client->pool_get_name(get_id(), &s);
+  return s;
+}
+
+std::string librados::IoCtx::get_pool_name() const
+{
+  return io_ctx_impl->get_cached_pool_name();
+}
+
+uint64_t librados::IoCtx::get_instance_id() const
+{
+  return io_ctx_impl->client->get_instance_id();
+}
+
+int librados::IoCtx::create(const std::string& oid, bool exclusive)
+{
+  object_t obj(oid);
+  return io_ctx_impl->create(obj, exclusive);
+}
+
+int librados::IoCtx::create(const std::string& oid, bool exclusive,
+                           const std::string& category) // unused
+{
+  object_t obj(oid);
+  return io_ctx_impl->create(obj, exclusive);
+}
+
+int librados::IoCtx::write(const std::string& oid, bufferlist& bl, size_t len, uint64_t off)
+{
+  object_t obj(oid);
+  return io_ctx_impl->write(obj, bl, len, off);
+}
+
+int librados::IoCtx::append(const std::string& oid, bufferlist& bl, size_t len)
+{
+  object_t obj(oid);
+  return io_ctx_impl->append(obj, bl, len);
+}
+
+int librados::IoCtx::write_full(const std::string& oid, bufferlist& bl)
+{
+  object_t obj(oid);
+  return io_ctx_impl->write_full(obj, bl);
+}
+
+int librados::IoCtx::writesame(const std::string& oid, bufferlist& bl,
+                              size_t write_len, uint64_t off)
+{
+  object_t obj(oid);
+  return io_ctx_impl->writesame(obj, bl, write_len, off);
+}
+
+
+int librados::IoCtx::read(const std::string& oid, bufferlist& bl, size_t len, uint64_t off)
+{
+  object_t obj(oid);
+  return io_ctx_impl->read(obj, bl, len, off);
+}
+
+int librados::IoCtx::checksum(const std::string& oid,
+                             rados_checksum_type_t type,
+                             const bufferlist &init_value_bl, size_t len,
+                             uint64_t off, size_t chunk_size, bufferlist *pbl)
+{
+  object_t obj(oid);
+  return io_ctx_impl->checksum(obj, get_checksum_op_type(type), init_value_bl,
+                              len, off, chunk_size, pbl);
+}
+
+int librados::IoCtx::remove(const std::string& oid)
+{
+  object_t obj(oid);
+  return io_ctx_impl->remove(obj);
+}
+
+int librados::IoCtx::remove(const std::string& oid, int flags)
+{
+  object_t obj(oid);
+  return io_ctx_impl->remove(obj, flags); 
+}
+
+int librados::IoCtx::trunc(const std::string& oid, uint64_t size)
+{
+  object_t obj(oid);
+  return io_ctx_impl->trunc(obj, size);
+}
+
+int librados::IoCtx::mapext(const std::string& oid, uint64_t off, size_t len,
+                           std::map<uint64_t,uint64_t>& m)
+{
+  object_t obj(oid);
+  return io_ctx_impl->mapext(obj, off, len, m);
+}
+
+int librados::IoCtx::cmpext(const std::string& oid, uint64_t off, bufferlist& cmp_bl)
+{
+  object_t obj(oid);
+  return io_ctx_impl->cmpext(obj, off, cmp_bl);
+}
+
+int librados::IoCtx::sparse_read(const std::string& oid, std::map<uint64_t,uint64_t>& m,
+                                bufferlist& bl, size_t len, uint64_t off)
+{
+  object_t obj(oid);
+  return io_ctx_impl->sparse_read(obj, m, bl, len, off);
+}
+
+int librados::IoCtx::getxattr(const std::string& oid, const char *name, bufferlist& bl)
+{
+  object_t obj(oid);
+  return io_ctx_impl->getxattr(obj, name, bl);
+}
+
+int librados::IoCtx::getxattrs(const std::string& oid, map<std::string, bufferlist>& attrset)
+{
+  object_t obj(oid);
+  return io_ctx_impl->getxattrs(obj, attrset);
+}
+
+int librados::IoCtx::setxattr(const std::string& oid, const char *name, bufferlist& bl)
+{
+  object_t obj(oid);
+  return io_ctx_impl->setxattr(obj, name, bl);
+}
+
+int librados::IoCtx::rmxattr(const std::string& oid, const char *name)
+{
+  object_t obj(oid);
+  return io_ctx_impl->rmxattr(obj, name);
+}
+
+int librados::IoCtx::stat(const std::string& oid, uint64_t *psize, time_t *pmtime)
+{
+  object_t obj(oid);
+  return io_ctx_impl->stat(obj, psize, pmtime);
+}
+
+int librados::IoCtx::stat2(const std::string& oid, uint64_t *psize, struct timespec *pts)
+{
+  object_t obj(oid);
+  return io_ctx_impl->stat2(obj, psize, pts);
+}
+
+int librados::IoCtx::exec(const std::string& oid, const char *cls, const char *method,
+                         bufferlist& inbl, bufferlist& outbl)
+{
+  object_t obj(oid);
+  return io_ctx_impl->exec(obj, cls, method, inbl, outbl);
+}
+
+int librados::IoCtx::tmap_update(const std::string& oid, bufferlist& cmdbl)
+{
+  object_t obj(oid);
+  return io_ctx_impl->tmap_update(obj, cmdbl);
+}
+
+int librados::IoCtx::tmap_put(const std::string& oid, bufferlist& bl)
+{
+  object_t obj(oid);
+  return io_ctx_impl->tmap_put(obj, bl);
+}
+
+int librados::IoCtx::tmap_get(const std::string& oid, bufferlist& bl)
+{
+  object_t obj(oid);
+  return io_ctx_impl->tmap_get(obj, bl);
+}
+
+int librados::IoCtx::tmap_to_omap(const std::string& oid, bool nullok)
+{
+  object_t obj(oid);
+  return io_ctx_impl->tmap_to_omap(obj, nullok);
+}
+
+int librados::IoCtx::omap_get_vals(const std::string& oid,
+                                   const std::string& start_after,
+                                   uint64_t max_return,
+                                   std::map<std::string, bufferlist> *out_vals)
+{
+  return omap_get_vals(oid, start_after, string(), max_return, out_vals);
+}
+
+int librados::IoCtx::omap_get_vals2(
+  const std::string& oid,
+  const std::string& start_after,
+  uint64_t max_return,
+  std::map<std::string, bufferlist> *out_vals,
+  bool *pmore)
+{
+  ObjectReadOperation op;
+  int r;
+  op.omap_get_vals2(start_after, max_return, out_vals, pmore, &r);
+  bufferlist bl;
+  int ret = operate(oid, &op, &bl);
+  if (ret < 0)
+    return ret;
+  return r;
+}
+
+int librados::IoCtx::omap_get_keys(const std::string& oid,
+                                   const std::string& orig_start_after,
+                                   uint64_t max_return,
+                                   std::set<std::string> *out_keys)
+{
+  bool first = true;
+  string start_after = orig_start_after;
+  bool more = true;
+  while (max_return > 0 && more) {
+    std::set<std::string> out;
+    ObjectReadOperation op;
+    op.omap_get_keys2(start_after, max_return, &out, &more, nullptr);
+    bufferlist bl;
+    int ret = operate(oid, &op, &bl);
+    if (ret < 0) {
+      return ret;
+    }
+    if (more) {
+      if (out.empty()) {
+       return -EINVAL;  // wth
+      }
+      start_after = *out.rbegin();
+    }
+    if (out.size() <= max_return) {
+      max_return -= out.size();
+    } else {
+      max_return = 0;
+    }
+    if (first) {
+      out_keys->swap(out);
+      first = false;
+    } else {
+      out_keys->insert(out.begin(), out.end());
+      out.clear();
+    }
+  }
+  return 0;
+}
+
+int librados::IoCtx::omap_get_keys2(
+  const std::string& oid,
+  const std::string& start_after,
+  uint64_t max_return,
+  std::set<std::string> *out_keys,
+  bool *pmore)
+{
+  ObjectReadOperation op;
+  int r;
+  op.omap_get_keys2(start_after, max_return, out_keys, pmore, &r);
+  bufferlist bl;
+  int ret = operate(oid, &op, &bl);
+  if (ret < 0)
+    return ret;
+  return r;
+}
+
+int librados::IoCtx::omap_get_header(const std::string& oid,
+                                     bufferlist *bl)
+{
+  ObjectReadOperation op;
+  int r;
+  op.omap_get_header(bl, &r);
+  bufferlist b;
+  int ret = operate(oid, &op, &b);
+  if (ret < 0)
+    return ret;
+
+  return r;
+}
+
+int librados::IoCtx::omap_get_vals_by_keys(const std::string& oid,
+                                           const std::set<std::string>& keys,
+                                           std::map<std::string, bufferlist> *vals)
+{
+  ObjectReadOperation op;
+  int r;
+  bufferlist bl;
+  op.omap_get_vals_by_keys(keys, vals, &r);
+  int ret = operate(oid, &op, &bl);
+  if (ret < 0)
+    return ret;
+
+  return r;
+}
+
+int librados::IoCtx::omap_set(const std::string& oid,
+                              const map<string, bufferlist>& m)
+{
+  ObjectWriteOperation op;
+  op.omap_set(m);
+  return operate(oid, &op);
+}
+
+int librados::IoCtx::omap_set_header(const std::string& oid,
+                                     const bufferlist& bl)
+{
+  ObjectWriteOperation op;
+  op.omap_set_header(bl);
+  return operate(oid, &op);
+}
+
+int librados::IoCtx::omap_clear(const std::string& oid)
+{
+  ObjectWriteOperation op;
+  op.omap_clear();
+  return operate(oid, &op);
+}
+
+int librados::IoCtx::omap_rm_keys(const std::string& oid,
+                                  const std::set<std::string>& keys)
+{
+  ObjectWriteOperation op;
+  op.omap_rm_keys(keys);
+  return operate(oid, &op);
+}
+
+int librados::IoCtx::operate(const std::string& oid, librados::ObjectWriteOperation *o)
+{
+  object_t obj(oid);
+  return io_ctx_impl->operate(obj, &o->impl->o, (ceph::real_time *)o->impl->prt);
+}
+
+int librados::IoCtx::operate(const std::string& oid, librados::ObjectReadOperation *o, bufferlist *pbl)
+{
+  object_t obj(oid);
+  return io_ctx_impl->operate_read(obj, &o->impl->o, pbl);
+}
+
+int librados::IoCtx::aio_operate(const std::string& oid, AioCompletion *c,
+                                librados::ObjectWriteOperation *o)
+{
+  object_t obj(oid);
+  return io_ctx_impl->aio_operate(obj, &o->impl->o, c->pc,
+                                 io_ctx_impl->snapc, 0);
+}
+int librados::IoCtx::aio_operate(const std::string& oid, AioCompletion *c,
+                                ObjectWriteOperation *o, int flags)
+{
+  object_t obj(oid);
+  return io_ctx_impl->aio_operate(obj, &o->impl->o, c->pc,
+                                 io_ctx_impl->snapc,
+                                 translate_flags(flags));
+}
+
+int librados::IoCtx::aio_operate(const std::string& oid, AioCompletion *c,
+                                librados::ObjectWriteOperation *o,
+                                snap_t snap_seq, std::vector<snap_t>& snaps)
+{
+  object_t obj(oid);
+  vector<snapid_t> snv;
+  snv.resize(snaps.size());
+  for (size_t i = 0; i < snaps.size(); ++i)
+    snv[i] = snaps[i];
+  SnapContext snapc(snap_seq, snv);
+  return io_ctx_impl->aio_operate(obj, &o->impl->o, c->pc,
+                                 snapc, 0);
+}
+
+int librados::IoCtx::aio_operate(const std::string& oid, AioCompletion *c,
+         librados::ObjectWriteOperation *o,
+         snap_t snap_seq, std::vector<snap_t>& snaps,
+         const blkin_trace_info *trace_info)
+{
+  object_t obj(oid);
+  vector<snapid_t> snv;
+  snv.resize(snaps.size());
+  for (size_t i = 0; i < snaps.size(); ++i)
+    snv[i] = snaps[i];
+  SnapContext snapc(snap_seq, snv);
+  return io_ctx_impl->aio_operate(obj, &o->impl->o, c->pc,
+          snapc, 0, trace_info);
+}
+
+int librados::IoCtx::aio_operate(const std::string& oid, AioCompletion *c,
+         librados::ObjectWriteOperation *o,
+         snap_t snap_seq, std::vector<snap_t>& snaps, int flags,
+         const blkin_trace_info *trace_info)
+{
+  object_t obj(oid);
+  vector<snapid_t> snv;
+  snv.resize(snaps.size());
+  for (size_t i = 0; i < snaps.size(); ++i)
+    snv[i] = snaps[i];
+  SnapContext snapc(snap_seq, snv);
+  return io_ctx_impl->aio_operate(obj, &o->impl->o, c->pc, snapc,
+                                  translate_flags(flags), trace_info);
+}
+
+int librados::IoCtx::aio_operate(const std::string& oid, AioCompletion *c,
+                                librados::ObjectReadOperation *o,
+                                bufferlist *pbl)
+{
+  object_t obj(oid);
+  return io_ctx_impl->aio_operate_read(obj, &o->impl->o, c->pc,
+                                      0, pbl);
+}
+
+// deprecated
+int librados::IoCtx::aio_operate(const std::string& oid, AioCompletion *c,
+                                librados::ObjectReadOperation *o, 
+                                snap_t snapid_unused_deprecated,
+                                int flags, bufferlist *pbl)
+{
+  object_t obj(oid);
+  int op_flags = 0;
+  if (flags & OPERATION_BALANCE_READS)
+    op_flags |= CEPH_OSD_FLAG_BALANCE_READS;
+  if (flags & OPERATION_LOCALIZE_READS)
+    op_flags |= CEPH_OSD_FLAG_LOCALIZE_READS;
+  if (flags & OPERATION_ORDER_READS_WRITES)
+    op_flags |= CEPH_OSD_FLAG_RWORDERED;
+
+  return io_ctx_impl->aio_operate_read(obj, &o->impl->o, c->pc,
+                                      op_flags, pbl);
+}
+
+int librados::IoCtx::aio_operate(const std::string& oid, AioCompletion *c,
+                                librados::ObjectReadOperation *o,
+                                int flags, bufferlist *pbl)
+{
+  object_t obj(oid);
+  return io_ctx_impl->aio_operate_read(obj, &o->impl->o, c->pc,
+                                      translate_flags(flags), pbl);
+}
+
+int librados::IoCtx::aio_operate(const std::string& oid, AioCompletion *c,
+         librados::ObjectReadOperation *o,
+         int flags, bufferlist *pbl, const blkin_trace_info *trace_info)
+{
+  object_t obj(oid);
+  return io_ctx_impl->aio_operate_read(obj, &o->impl->o, c->pc,
+               translate_flags(flags), pbl, trace_info);
+}
+
+void librados::IoCtx::snap_set_read(snap_t seq)
+{
+  io_ctx_impl->set_snap_read(seq);
+}
+
+int librados::IoCtx::selfmanaged_snap_set_write_ctx(snap_t seq, vector<snap_t>& snaps)
+{
+  vector<snapid_t> snv;
+  snv.resize(snaps.size());
+  for (unsigned i=0; i<snaps.size(); i++)
+    snv[i] = snaps[i];
+  return io_ctx_impl->set_snap_write_context(seq, snv);
+}
+
+int librados::IoCtx::snap_create(const char *snapname)
+{
+  return io_ctx_impl->snap_create(snapname);
+}
+
+int librados::IoCtx::snap_lookup(const char *name, snap_t *snapid)
+{
+  return io_ctx_impl->snap_lookup(name, snapid);
+}
+
+int librados::IoCtx::snap_get_stamp(snap_t snapid, time_t *t)
+{
+  return io_ctx_impl->snap_get_stamp(snapid, t);
+}
+
+int librados::IoCtx::snap_get_name(snap_t snapid, std::string *s)
+{
+  return io_ctx_impl->snap_get_name(snapid, s);
+}
+
+int librados::IoCtx::snap_remove(const char *snapname)
+{
+  return io_ctx_impl->snap_remove(snapname);
+}
+
+int librados::IoCtx::snap_list(std::vector<snap_t> *snaps)
+{
+  return io_ctx_impl->snap_list(snaps);
+}
+
+int librados::IoCtx::snap_rollback(const std::string& oid, const char *snapname)
+{
+  return io_ctx_impl->rollback(oid, snapname);
+}
+
+// Deprecated name kept for backward compatibility
+int librados::IoCtx::rollback(const std::string& oid, const char *snapname)
+{
+  return snap_rollback(oid, snapname);
+}
+
+int librados::IoCtx::selfmanaged_snap_create(uint64_t *snapid)
+{
+  return io_ctx_impl->selfmanaged_snap_create(snapid);
+}
+
+void librados::IoCtx::aio_selfmanaged_snap_create(uint64_t *snapid,
+                                                  AioCompletion *c)
+{
+  io_ctx_impl->aio_selfmanaged_snap_create(snapid, c->pc);
+}
+
+int librados::IoCtx::selfmanaged_snap_remove(uint64_t snapid)
+{
+  return io_ctx_impl->selfmanaged_snap_remove(snapid);
+}
+
+void librados::IoCtx::aio_selfmanaged_snap_remove(uint64_t snapid,
+                                                  AioCompletion *c)
+{
+  io_ctx_impl->aio_selfmanaged_snap_remove(snapid, c->pc);
+}
+
+int librados::IoCtx::selfmanaged_snap_rollback(const std::string& oid, uint64_t snapid)
+{
+  return io_ctx_impl->selfmanaged_snap_rollback_object(oid,
+                                                      io_ctx_impl->snapc,
+                                                      snapid);
+}
+
+int librados::IoCtx::lock_exclusive(const std::string &oid, const std::string &name,
+                                   const std::string &cookie,
+                                   const std::string &description,
+                                   struct timeval * duration, uint8_t flags)
+{
+  utime_t dur = utime_t();
+  if (duration)
+    dur.set_from_timeval(duration);
+
+  return rados::cls::lock::lock(this, oid, name, LOCK_EXCLUSIVE, cookie, "",
+                               description, dur, flags);
+}
+
+int librados::IoCtx::lock_shared(const std::string &oid, const std::string &name,
+                                const std::string &cookie, const std::string &tag,
+                                const std::string &description,
+                                struct timeval * duration, uint8_t flags)
+{
+  utime_t dur = utime_t();
+  if (duration)
+    dur.set_from_timeval(duration);
+
+  return rados::cls::lock::lock(this, oid, name, LOCK_SHARED, cookie, tag,
+                               description, dur, flags);
+}
+
+int librados::IoCtx::unlock(const std::string &oid, const std::string &name,
+                           const std::string &cookie)
+{
+  return rados::cls::lock::unlock(this, oid, name, cookie);
+}
+
+struct AioUnlockCompletion : public librados::ObjectOperationCompletion {
+  librados::AioCompletionImpl *completion;
+  AioUnlockCompletion(librados::AioCompletion *c) : completion(c->pc) {
+    completion->get();
+  };
+  void handle_completion(int r, bufferlist& outbl) override {
+    rados_callback_t cb = completion->callback_complete;
+    void *cb_arg = completion->callback_complete_arg;
+    cb(completion, cb_arg);
+    completion->lock.Lock();
+    completion->callback_complete = NULL;
+    completion->cond.Signal();
+    completion->put_unlock();
+  }
+};
+
+int librados::IoCtx::aio_unlock(const std::string &oid, const std::string &name,
+                               const std::string &cookie, AioCompletion *c)
+{
+  return rados::cls::lock::aio_unlock(this, oid, name, cookie, c);
+}
+
+int librados::IoCtx::break_lock(const std::string &oid, const std::string &name,
+                               const std::string &client, const std::string &cookie)
+{
+  entity_name_t locker;
+  if (!locker.parse(client))
+    return -EINVAL;
+  return rados::cls::lock::break_lock(this, oid, name, cookie, locker);
+}
+
+int librados::IoCtx::list_lockers(const std::string &oid, const std::string &name,
+                                 int *exclusive,
+                                 std::string *tag,
+                                 std::list<librados::locker_t> *lockers)
+{
+  std::list<librados::locker_t> tmp_lockers;
+  map<rados::cls::lock::locker_id_t, rados::cls::lock::locker_info_t> rados_lockers;
+  std::string tmp_tag;
+  ClsLockType tmp_type;
+  int r = rados::cls::lock::get_lock_info(this, oid, name, &rados_lockers, &tmp_type, &tmp_tag);
+  if (r < 0)
+         return r;
+
+  map<rados::cls::lock::locker_id_t, rados::cls::lock::locker_info_t>::iterator map_it;
+  for (map_it = rados_lockers.begin(); map_it != rados_lockers.end(); ++map_it) {
+    librados::locker_t locker;
+    locker.client = stringify(map_it->first.locker);
+    locker.cookie = map_it->first.cookie;
+    locker.address = stringify(map_it->second.addr);
+    tmp_lockers.push_back(locker);
+  }
+
+  if (lockers)
+    *lockers = tmp_lockers;
+  if (tag)
+    *tag = tmp_tag;
+  if (exclusive) {
+    if (tmp_type == LOCK_EXCLUSIVE)
+      *exclusive = 1;
+    else
+      *exclusive = 0;
+  }
+
+  return tmp_lockers.size();
+}
+
+librados::NObjectIterator librados::IoCtx::nobjects_begin()
+{
+  bufferlist bl;
+  return nobjects_begin(bl);
+}
+
+librados::NObjectIterator librados::IoCtx::nobjects_begin(
+    const bufferlist &filter)
+{
+  rados_list_ctx_t listh;
+  rados_nobjects_list_open(io_ctx_impl, &listh);
+  NObjectIterator iter((ObjListCtx*)listh);
+  if (filter.length() > 0) {
+    iter.set_filter(filter);
+  }
+  iter.get_next();
+  return iter;
+}
+
+librados::NObjectIterator librados::IoCtx::nobjects_begin(uint32_t pos)
+{
+  bufferlist bl;
+  return nobjects_begin(pos, bl);
+}
+
+librados::NObjectIterator librados::IoCtx::nobjects_begin(
+  uint32_t pos, const bufferlist &filter)
+{
+  rados_list_ctx_t listh;
+  rados_nobjects_list_open(io_ctx_impl, &listh);
+  NObjectIterator iter((ObjListCtx*)listh);
+  if (filter.length() > 0) {
+    iter.set_filter(filter);
+  }
+  iter.seek(pos);
+  return iter;
+}
+
+librados::NObjectIterator librados::IoCtx::nobjects_begin(const ObjectCursor& cursor)
+{
+  bufferlist bl;
+  return nobjects_begin(cursor, bl);
+}
+
+librados::NObjectIterator librados::IoCtx::nobjects_begin(
+  const ObjectCursor& cursor, const bufferlist &filter)
+{
+  rados_list_ctx_t listh;
+  rados_nobjects_list_open(io_ctx_impl, &listh);
+  NObjectIterator iter((ObjListCtx*)listh);
+  if (filter.length() > 0) {
+    iter.set_filter(filter);
+  }
+  iter.seek(cursor);
+  return iter;
+}
+
+const librados::NObjectIterator& librados::IoCtx::nobjects_end() const
+{
+  return NObjectIterator::__EndObjectIterator;
+}
+
+int librados::IoCtx::hit_set_list(uint32_t hash, AioCompletion *c,
+                                 std::list< std::pair<time_t, time_t> > *pls)
+{
+  return io_ctx_impl->hit_set_list(hash, c->pc, pls);
+}
+
+int librados::IoCtx::hit_set_get(uint32_t hash,  AioCompletion *c, time_t stamp,
+                                bufferlist *pbl)
+{
+  return io_ctx_impl->hit_set_get(hash, c->pc, stamp, pbl);
+}
+
+
+
+uint64_t librados::IoCtx::get_last_version()
+{
+  return io_ctx_impl->last_version();
+}
+
+int librados::IoCtx::aio_read(const std::string& oid, librados::AioCompletion *c,
+                             bufferlist *pbl, size_t len, uint64_t off)
+{
+  return io_ctx_impl->aio_read(oid, c->pc, pbl, len, off,
+                              io_ctx_impl->snap_seq);
+}
+
+int librados::IoCtx::aio_read(const std::string& oid, librados::AioCompletion *c,
+                             bufferlist *pbl, size_t len, uint64_t off,
+                             uint64_t snapid)
+{
+  return io_ctx_impl->aio_read(oid, c->pc, pbl, len, off, snapid);
+}
+
+int librados::IoCtx::aio_exec(const std::string& oid,
+                             librados::AioCompletion *c, const char *cls,
+                             const char *method, bufferlist& inbl,
+                             bufferlist *outbl)
+{
+  object_t obj(oid);
+  return io_ctx_impl->aio_exec(obj, c->pc, cls, method, inbl, outbl);
+}
+
+int librados::IoCtx::aio_cmpext(const std::string& oid,
+                               librados::AioCompletion *c,
+                               uint64_t off,
+                               bufferlist& cmp_bl)
+{
+  return io_ctx_impl->aio_cmpext(oid, c->pc, off, cmp_bl);
+}
+
+int librados::IoCtx::aio_sparse_read(const std::string& oid, librados::AioCompletion *c,
+                                    std::map<uint64_t,uint64_t> *m, bufferlist *data_bl,
+                                    size_t len, uint64_t off)
+{
+  return io_ctx_impl->aio_sparse_read(oid, c->pc,
+                                     m, data_bl, len, off,
+                                     io_ctx_impl->snap_seq);
+}
+
+int librados::IoCtx::aio_sparse_read(const std::string& oid, librados::AioCompletion *c,
+                                    std::map<uint64_t,uint64_t> *m, bufferlist *data_bl,
+                                    size_t len, uint64_t off, uint64_t snapid)
+{
+  return io_ctx_impl->aio_sparse_read(oid, c->pc,
+                                     m, data_bl, len, off, snapid);
+}
+
+int librados::IoCtx::aio_write(const std::string& oid, librados::AioCompletion *c,
+                              const bufferlist& bl, size_t len, uint64_t off)
+{
+  return io_ctx_impl->aio_write(oid, c->pc, bl, len, off);
+}
+
+int librados::IoCtx::aio_append(const std::string& oid, librados::AioCompletion *c,
+                               const bufferlist& bl, size_t len)
+{
+  return io_ctx_impl->aio_append(oid, c->pc, bl, len);
+}
+
+int librados::IoCtx::aio_write_full(const std::string& oid, librados::AioCompletion *c,
+                                   const bufferlist& bl)
+{
+  object_t obj(oid);
+  return io_ctx_impl->aio_write_full(obj, c->pc, bl);
+}
+
+int librados::IoCtx::aio_writesame(const std::string& oid, librados::AioCompletion *c,
+                                  const bufferlist& bl, size_t write_len,
+                                  uint64_t off)
+{
+  return io_ctx_impl->aio_writesame(oid, c->pc, bl, write_len, off);
+}
+
+
+int librados::IoCtx::aio_remove(const std::string& oid, librados::AioCompletion *c)
+{
+  return io_ctx_impl->aio_remove(oid, c->pc);
+}
+
+int librados::IoCtx::aio_remove(const std::string& oid, librados::AioCompletion *c, int flags)
+{
+  return io_ctx_impl->aio_remove(oid, c->pc, flags);
+}
+
+int librados::IoCtx::aio_flush_async(librados::AioCompletion *c)
+{
+  io_ctx_impl->flush_aio_writes_async(c->pc);
+  return 0;
+}
+
+int librados::IoCtx::aio_flush()
+{
+  io_ctx_impl->flush_aio_writes();
+  return 0;
+}
+
+struct AioGetxattrDataPP {
+  AioGetxattrDataPP(librados::AioCompletionImpl *c, bufferlist *_bl) :
+    bl(_bl), completion(c) {}
+  bufferlist *bl;
+  struct librados::C_AioCompleteAndSafe completion;
+};
+
+static void rados_aio_getxattr_completepp(rados_completion_t c, void *arg) {
+  AioGetxattrDataPP *cdata = reinterpret_cast<AioGetxattrDataPP*>(arg);
+  int rc = rados_aio_get_return_value(c);
+  if (rc >= 0) {
+    rc = cdata->bl->length();
+  }
+  cdata->completion.finish(rc);
+  delete cdata;
+}
+
+int librados::IoCtx::aio_getxattr(const std::string& oid, librados::AioCompletion *c,
+                                 const char *name, bufferlist& bl)
+{
+  // create data object to be passed to async callback
+  AioGetxattrDataPP *cdata = new AioGetxattrDataPP(c->pc, &bl);
+  if (!cdata) {
+    return -ENOMEM;
+  }
+  // create completion callback
+  librados::AioCompletionImpl *comp = new librados::AioCompletionImpl;
+  comp->set_complete_callback(cdata, rados_aio_getxattr_completepp);
+  // call actual getxattr from IoCtxImpl
+  object_t obj(oid);
+  return io_ctx_impl->aio_getxattr(obj, comp, name, bl);
+}
+
+int librados::IoCtx::aio_getxattrs(const std::string& oid, AioCompletion *c,
+                                  map<std::string, bufferlist>& attrset)
+{
+  object_t obj(oid);
+  return io_ctx_impl->aio_getxattrs(obj, c->pc, attrset);
+}
+
+int librados::IoCtx::aio_setxattr(const std::string& oid, AioCompletion *c,
+                                 const char *name, bufferlist& bl)
+{
+  object_t obj(oid);
+  return io_ctx_impl->aio_setxattr(obj, c->pc, name, bl);
+}
+
+int librados::IoCtx::aio_rmxattr(const std::string& oid, AioCompletion *c,
+                                const char *name)
+{
+  object_t obj(oid);
+  return io_ctx_impl->aio_rmxattr(obj, c->pc, name);
+}
+
+int librados::IoCtx::aio_stat(const std::string& oid, librados::AioCompletion *c,
+                             uint64_t *psize, time_t *pmtime)
+{
+  object_t obj(oid);
+  return io_ctx_impl->aio_stat(obj, c->pc, psize, pmtime);
+}
+
+int librados::IoCtx::aio_cancel(librados::AioCompletion *c)
+{
+  return io_ctx_impl->aio_cancel(c->pc);
+}
+
+int librados::IoCtx::watch(const string& oid, uint64_t ver, uint64_t *cookie,
+                          librados::WatchCtx *ctx)
+{
+  object_t obj(oid);
+  return io_ctx_impl->watch(obj, cookie, ctx, NULL);
+}
+
+int librados::IoCtx::watch2(const string& oid, uint64_t *cookie,
+                           librados::WatchCtx2 *ctx2)
+{
+  object_t obj(oid);
+  return io_ctx_impl->watch(obj, cookie, NULL, ctx2);
+}
+
+int librados::IoCtx::watch3(const string& oid, uint64_t *cookie,
+          librados::WatchCtx2 *ctx2, uint32_t timeout)
+{
+  object_t obj(oid);
+  return io_ctx_impl->watch(obj, cookie, NULL, ctx2, timeout);
+}
+
+int librados::IoCtx::aio_watch(const string& oid, AioCompletion *c,
+                               uint64_t *cookie,
+                               librados::WatchCtx2 *ctx2)
+{
+  object_t obj(oid);
+  return io_ctx_impl->aio_watch(obj, c->pc, cookie, NULL, ctx2);
+}
+
+int librados::IoCtx::aio_watch2(const string& oid, AioCompletion *c,
+                                uint64_t *cookie,
+                                librados::WatchCtx2 *ctx2,
+                                uint32_t timeout)
+{
+  object_t obj(oid);
+  return io_ctx_impl->aio_watch(obj, c->pc, cookie, NULL, ctx2, timeout);
+}
+
+int librados::IoCtx::unwatch(const string& oid, uint64_t handle)
+{
+  return io_ctx_impl->unwatch(handle);
+}
+
+int librados::IoCtx::unwatch2(uint64_t handle)
+{
+  return io_ctx_impl->unwatch(handle);
+}
+
+int librados::IoCtx::aio_unwatch(uint64_t handle, AioCompletion *c)
+{
+  return io_ctx_impl->aio_unwatch(handle, c->pc);
+}
+
+int librados::IoCtx::watch_check(uint64_t handle)
+{
+  return io_ctx_impl->watch_check(handle);
+}
+
+int librados::IoCtx::notify(const string& oid, uint64_t ver, bufferlist& bl)
+{
+  object_t obj(oid);
+  return io_ctx_impl->notify(obj, bl, 0, NULL, NULL, NULL);
+}
+
+int librados::IoCtx::notify2(const string& oid, bufferlist& bl,
+                            uint64_t timeout_ms, bufferlist *preplybl)
+{
+  object_t obj(oid);
+  return io_ctx_impl->notify(obj, bl, timeout_ms, preplybl, NULL, NULL);
+}
+
+int librados::IoCtx::aio_notify(const string& oid, AioCompletion *c,
+                                bufferlist& bl, uint64_t timeout_ms,
+                                bufferlist *preplybl)
+{
+  object_t obj(oid);
+  return io_ctx_impl->aio_notify(obj, c->pc, bl, timeout_ms, preplybl, NULL,
+                                 NULL);
+}
+
+void librados::IoCtx::notify_ack(const std::string& o,
+                                uint64_t notify_id, uint64_t handle,
+                                bufferlist& bl)
+{
+  io_ctx_impl->notify_ack(o, notify_id, handle, bl);
+}
+
+int librados::IoCtx::list_watchers(const std::string& oid,
+                                   std::list<obj_watch_t> *out_watchers)
+{
+  ObjectReadOperation op;
+  int r;
+  op.list_watchers(out_watchers, &r);
+  bufferlist bl;
+  int ret = operate(oid, &op, &bl);
+  if (ret < 0)
+    return ret;
+
+  return r;
+}
+
+int librados::IoCtx::list_snaps(const std::string& oid,
+                                   snap_set_t *out_snaps)
+{
+  ObjectReadOperation op;
+  int r;
+  if (io_ctx_impl->snap_seq != CEPH_SNAPDIR)
+    return -EINVAL;
+  op.list_snaps(out_snaps, &r);
+  bufferlist bl;
+  int ret = operate(oid, &op, &bl);
+  if (ret < 0)
+    return ret;
+
+  return r;
+}
+
+void librados::IoCtx::set_notify_timeout(uint32_t timeout)
+{
+  io_ctx_impl->set_notify_timeout(timeout);
+}
+
+int librados::IoCtx::set_alloc_hint(const std::string& o,
+                                    uint64_t expected_object_size,
+                                    uint64_t expected_write_size)
+{
+  object_t oid(o);
+  return io_ctx_impl->set_alloc_hint(oid, expected_object_size,
+                                     expected_write_size, 0);
+}
+
+int librados::IoCtx::set_alloc_hint2(const std::string& o,
+                                    uint64_t expected_object_size,
+                                    uint64_t expected_write_size,
+                                    uint32_t flags)
+{
+  object_t oid(o);
+  return io_ctx_impl->set_alloc_hint(oid, expected_object_size,
+                                     expected_write_size, flags);
+}
+
+void librados::IoCtx::set_assert_version(uint64_t ver)
+{
+  io_ctx_impl->set_assert_version(ver);
+}
+
+void librados::IoCtx::locator_set_key(const string& key)
+{
+  io_ctx_impl->oloc.key = key;
+}
+
+void librados::IoCtx::set_namespace(const string& nspace)
+{
+  io_ctx_impl->oloc.nspace = nspace;
+}
+
+std::string librados::IoCtx::get_namespace() const
+{
+  return io_ctx_impl->oloc.nspace;
+}
+
+int64_t librados::IoCtx::get_id()
+{
+  return io_ctx_impl->get_id();
+}
+
+uint32_t librados::IoCtx::get_object_hash_position(const std::string& oid)
+{
+  uint32_t hash;
+  int r = io_ctx_impl->get_object_hash_position(oid, &hash);
+  if (r < 0)
+    hash = 0;
+  return hash;
+}
+
+uint32_t librados::IoCtx::get_object_pg_hash_position(const std::string& oid)
+{
+  uint32_t hash;
+  int r = io_ctx_impl->get_object_pg_hash_position(oid, &hash);
+  if (r < 0)
+    hash = 0;
+  return hash;
+}
+
+int librados::IoCtx::get_object_hash_position2(
+    const std::string& oid, uint32_t *hash_position)
+{
+  return io_ctx_impl->get_object_hash_position(oid, hash_position);
+}
+
+int librados::IoCtx::get_object_pg_hash_position2(
+    const std::string& oid, uint32_t *pg_hash_position)
+{
+  return io_ctx_impl->get_object_pg_hash_position(oid, pg_hash_position);
+}
+
+librados::config_t librados::IoCtx::cct()
+{
+  return (config_t)io_ctx_impl->client->cct;
+}
+
+librados::IoCtx::IoCtx(IoCtxImpl *io_ctx_impl_)
+  : io_ctx_impl(io_ctx_impl_)
+{
+}
+
+void librados::IoCtx::set_osdmap_full_try()
+{
+  io_ctx_impl->objecter->set_osdmap_full_try();
+}
+
+void librados::IoCtx::unset_osdmap_full_try()
+{
+  io_ctx_impl->objecter->unset_osdmap_full_try();
+}
+
+///////////////////////////// Rados //////////////////////////////
+void librados::Rados::version(int *major, int *minor, int *extra)
+{
+  rados_version(major, minor, extra);
+}
+
+librados::Rados::Rados() : client(NULL)
+{
+}
+
+librados::Rados::Rados(IoCtx &ioctx)
+{
+  client = ioctx.io_ctx_impl->client;
+  ceph_assert(client != NULL);
+  client->get();
+}
+
+librados::Rados::~Rados()
+{
+  shutdown();
+}
+
+int librados::Rados::init(const char * const id)
+{
+  return rados_create((rados_t *)&client, id);
+}
+
+int librados::Rados::init2(const char * const name,
+                          const char * const clustername, uint64_t flags)
+{
+  return rados_create2((rados_t *)&client, clustername, name, flags);
+}
+
+int librados::Rados::init_with_context(config_t cct_)
+{
+  return rados_create_with_context((rados_t *)&client, (rados_config_t)cct_);
+}
+
+int librados::Rados::connect()
+{
+  return client->connect();
+}
+
+librados::config_t librados::Rados::cct()
+{
+  return (config_t)client->cct;
+}
+
+int librados::Rados::watch_flush()
+{
+  if (!client)
+    return -EINVAL;
+  return client->watch_flush();
+}
+
+int librados::Rados::aio_watch_flush(AioCompletion *c)
+{
+  if (!client)
+    return -EINVAL;
+  return client->async_watch_flush(c->pc);
+}
+
+void librados::Rados::shutdown()
+{
+  if (!client)
+    return;
+  if (client->put()) {
+    client->shutdown();
+    delete client;
+    client = NULL;
+  }
+}
+
+uint64_t librados::Rados::get_instance_id()
+{
+  return client->get_instance_id();
+}
+
+int librados::Rados::get_min_compatible_osd(int8_t* require_osd_release)
+{
+  return client->get_min_compatible_osd(require_osd_release);
+}
+
+int librados::Rados::get_min_compatible_client(int8_t* min_compat_client,
+                                               int8_t* require_min_compat_client)
+{
+  return client->get_min_compatible_client(min_compat_client,
+                                           require_min_compat_client);
+}
+
+int librados::Rados::conf_read_file(const char * const path) const
+{
+  return rados_conf_read_file((rados_t)client, path);
+}
+
+int librados::Rados::conf_parse_argv(int argc, const char ** argv) const
+{
+  return rados_conf_parse_argv((rados_t)client, argc, argv);
+}
+
+int librados::Rados::conf_parse_argv_remainder(int argc, const char ** argv,
+                                              const char ** remargv) const
+{
+  return rados_conf_parse_argv_remainder((rados_t)client, argc, argv, remargv);
+}
+
+int librados::Rados::conf_parse_env(const char *name) const
+{
+  return rados_conf_parse_env((rados_t)client, name);
+}
+
+int librados::Rados::conf_set(const char *option, const char *value)
+{
+  return rados_conf_set((rados_t)client, option, value);
+}
+
+int librados::Rados::conf_get(const char *option, std::string &val)
+{
+  char *str = NULL;
+  const auto& conf = client->cct->_conf;
+  int ret = conf.get_val(option, &str, -1);
+  if (ret) {
+    free(str);
+    return ret;
+  }
+  val = str;
+  free(str);
+  return 0;
+}
+
+int librados::Rados::service_daemon_register(
+  const std::string& service,  ///< service name (e.g., 'rgw')
+  const std::string& name,     ///< daemon name (e.g., 'gwfoo')
+  const std::map<std::string,std::string>& metadata) ///< static metadata about daemon
+{
+  return client->service_daemon_register(service, name, metadata);
+}
+
+int librados::Rados::service_daemon_update_status(
+  std::map<std::string,std::string>&& status)
+{
+  return client->service_daemon_update_status(std::move(status));
+}
+
+int librados::Rados::pool_create(const char *name)
+{
+  string str(name);
+  return client->pool_create(str);
+}
+
+int librados::Rados::pool_create(const char *name, uint64_t auid)
+{
+  if (auid != CEPH_AUTH_UID_DEFAULT) {
+    return -EINVAL;
+  }
+  string str(name);
+  return client->pool_create(str);
+}
+
+int librados::Rados::pool_create(const char *name, uint64_t auid, __u8 crush_rule)
+{
+  if (auid != CEPH_AUTH_UID_DEFAULT) {
+    return -EINVAL;
+  }
+  string str(name);
+  return client->pool_create(str, crush_rule);
+}
+
+int librados::Rados::pool_create_with_rule(const char *name, __u8 crush_rule)
+{
+  string str(name);
+  return client->pool_create(str, crush_rule);
+}
+
+int librados::Rados::pool_create_async(const char *name, PoolAsyncCompletion *c)
+{
+  string str(name);
+  return client->pool_create_async(str, c->pc);
+}
+
+int librados::Rados::pool_create_async(const char *name, uint64_t auid, PoolAsyncCompletion *c)
+{
+  if (auid != CEPH_AUTH_UID_DEFAULT) {
+    return -EINVAL;
+  }
+  string str(name);
+  return client->pool_create_async(str, c->pc);
+}
+
+int librados::Rados::pool_create_async(const char *name, uint64_t auid, __u8 crush_rule,
+                                      PoolAsyncCompletion *c)
+{
+  if (auid != CEPH_AUTH_UID_DEFAULT) {
+    return -EINVAL;
+  }
+  string str(name);
+  return client->pool_create_async(str, c->pc, crush_rule);
+}
+
+int librados::Rados::pool_create_with_rule_async(
+  const char *name, __u8 crush_rule,
+  PoolAsyncCompletion *c)
+{
+  string str(name);
+  return client->pool_create_async(str, c->pc, crush_rule);
+}
+
+int librados::Rados::pool_get_base_tier(int64_t pool_id, int64_t* base_tier)
+{
+  tracepoint(librados, rados_pool_get_base_tier_enter, (rados_t)client, pool_id);
+  int retval = client->pool_get_base_tier(pool_id, base_tier);
+  tracepoint(librados, rados_pool_get_base_tier_exit, retval, *base_tier);
+  return retval;
+}
+
+int librados::Rados::pool_delete(const char *name)
+{
+  return client->pool_delete(name);
+}
+
+int librados::Rados::pool_delete_async(const char *name, PoolAsyncCompletion *c)
+{
+  return client->pool_delete_async(name, c->pc);
+}
+
+int librados::Rados::pool_list(std::list<std::string>& v)
+{
+  std::list<std::pair<int64_t, std::string> > pools;
+  int r = client->pool_list(pools);
+  if (r < 0) {
+    return r;
+  }
+
+  v.clear();
+  for (std::list<std::pair<int64_t, std::string> >::iterator it = pools.begin();
+       it != pools.end(); ++it) {
+    v.push_back(it->second);
+  }
+  return 0;
+}
+
+int librados::Rados::pool_list2(std::list<std::pair<int64_t, std::string> >& v)
+{
+  return client->pool_list(v);
+}
+
+int64_t librados::Rados::pool_lookup(const char *name)
+{
+  return client->lookup_pool(name);
+}
+
+int librados::Rados::pool_reverse_lookup(int64_t id, std::string *name)
+{
+  return client->pool_get_name(id, name);
+}
+
+int librados::Rados::mon_command(string cmd, const bufferlist& inbl,
+                                bufferlist *outbl, string *outs)
+{
+  vector<string> cmdvec;
+  cmdvec.push_back(cmd);
+  return client->mon_command(cmdvec, inbl, outbl, outs);
+}
+
+int librados::Rados::osd_command(int osdid, std::string cmd, const bufferlist& inbl,
+                                 bufferlist *outbl, std::string *outs)
+{
+  vector<string> cmdvec;
+  cmdvec.push_back(cmd);
+  return client->osd_command(osdid, cmdvec, inbl, outbl, outs);
+}
+
+int librados::Rados::mgr_command(std::string cmd, const bufferlist& inbl,
+                                 bufferlist *outbl, std::string *outs)
+{
+  vector<string> cmdvec;
+  cmdvec.push_back(cmd);
+  return client->mgr_command(cmdvec, inbl, outbl, outs);
+}
+
+
+
+int librados::Rados::pg_command(const char *pgstr, std::string cmd, const bufferlist& inbl,
+                                bufferlist *outbl, std::string *outs)
+{
+  vector<string> cmdvec;
+  cmdvec.push_back(cmd);
+
+  pg_t pgid;
+  if (!pgid.parse(pgstr))
+    return -EINVAL;
+
+  return client->pg_command(pgid, cmdvec, inbl, outbl, outs);
+}
+
+int librados::Rados::ioctx_create(const char *name, IoCtx &io)
+{
+  rados_ioctx_t p;
+  int ret = rados_ioctx_create((rados_t)client, name, &p);
+  if (ret)
+    return ret;
+  io.close();
+  io.io_ctx_impl = (IoCtxImpl*)p;
+  return 0;
+}
+
+int librados::Rados::ioctx_create2(int64_t pool_id, IoCtx &io)
+{
+  rados_ioctx_t p;
+  int ret = rados_ioctx_create2((rados_t)client, pool_id, &p);
+  if (ret)
+    return ret;
+  io.close();
+  io.io_ctx_impl = (IoCtxImpl*)p;
+  return 0;
+}
+
+void librados::Rados::test_blacklist_self(bool set)
+{
+  client->blacklist_self(set);
+}
+
+int librados::Rados::get_pool_stats(std::list<string>& v,
+                                   stats_map& result)
+{
+  map<string,::pool_stat_t> rawresult;
+  int r = client->get_pool_stats(v, rawresult);
+  for (map<string,::pool_stat_t>::iterator p = rawresult.begin();
+       p != rawresult.end();
+       ++p) {
+    pool_stat_t& pv = result[p->first];
+    object_stat_sum_t *sum = &p->second.stats.sum;
+    pv.num_kb = shift_round_up(sum->num_bytes, 10);
+    pv.num_bytes = sum->num_bytes;
+    pv.num_objects = sum->num_objects;
+    pv.num_object_clones = sum->num_object_clones;
+    pv.num_object_copies = sum->num_object_copies;
+    pv.num_objects_missing_on_primary = sum->num_objects_missing_on_primary;
+    pv.num_objects_unfound = sum->num_objects_unfound;
+    pv.num_objects_degraded = sum->num_objects_degraded;
+    pv.num_rd = sum->num_rd;
+    pv.num_rd_kb = sum->num_rd_kb;
+    pv.num_wr = sum->num_wr;
+    pv.num_wr_kb = sum->num_wr_kb;
+  }
+  return r;
+}
+
+int librados::Rados::get_pool_stats(std::list<string>& v,
+                                   std::map<string, stats_map>& result)
+{
+  stats_map m;
+  int r = get_pool_stats(v, m);
+  if (r < 0)
+    return r;
+  for (map<string,pool_stat_t>::iterator p = m.begin();
+       p != m.end();
+       ++p) {
+    result[p->first][string()] = p->second;
+  }
+  return r;
+}
+
+int librados::Rados::get_pool_stats(std::list<string>& v,
+                                   string& category, // unused
+                                   std::map<string, stats_map>& result)
+{
+  return -EOPNOTSUPP;
+}
+
+bool librados::Rados::get_pool_is_selfmanaged_snaps_mode(const std::string& pool)
+{
+  return client->get_pool_is_selfmanaged_snaps_mode(pool);
+}
+
+int librados::Rados::cluster_stat(cluster_stat_t& result)
+{
+  ceph_statfs stats;
+  int r = client->get_fs_stats(stats);
+  result.kb = stats.kb;
+  result.kb_used = stats.kb_used;
+  result.kb_avail = stats.kb_avail;
+  result.num_objects = stats.num_objects;
+  return r;
+}
+
+int librados::Rados::cluster_fsid(string *fsid)
+{
+  return client->get_fsid(fsid);
+}
+
+namespace librados {
+  struct PlacementGroupImpl {
+    pg_t pgid;
+  };
+
+  PlacementGroup::PlacementGroup()
+    : impl{new PlacementGroupImpl}
+  {}
+
+  PlacementGroup::PlacementGroup(const PlacementGroup& pg)
+    : impl{new PlacementGroupImpl}
+  {
+    impl->pgid = pg.impl->pgid;
+  }
+
+  PlacementGroup::~PlacementGroup()
+  {}
+
+  bool PlacementGroup::parse(const char* s)
+  {
+    return impl->pgid.parse(s);
+  }
+}
+
+std::ostream& librados::operator<<(std::ostream& out,
+                                  const librados::PlacementGroup& pg)
+{
+  return out << pg.impl->pgid;
+}
+
+int librados::Rados::get_inconsistent_pgs(int64_t pool_id,
+                                         std::vector<PlacementGroup>* pgs)
+{
+  std::vector<string> pgids;
+  if (auto ret = client->get_inconsistent_pgs(pool_id, &pgids); ret) {
+    return ret;
+  }
+  for (const auto& pgid : pgids) {
+    librados::PlacementGroup pg;
+    if (!pg.parse(pgid.c_str())) {
+      return -EINVAL;
+    }
+    pgs->emplace_back(pg);
+  }
+  return 0;
+}
+
+int librados::Rados::get_inconsistent_objects(const PlacementGroup& pg,
+                                             const object_id_t &start_after,
+                                             unsigned max_return,
+                                             AioCompletion *c,
+                                             std::vector<inconsistent_obj_t>* objects,
+                                             uint32_t* interval)
+{
+  IoCtx ioctx;
+  const pg_t pgid = pg.impl->pgid;
+  int r = ioctx_create2(pgid.pool(), ioctx);
+  if (r < 0) {
+    return r;
+  }
+
+  return ioctx.io_ctx_impl->get_inconsistent_objects(pgid,
+                                                    start_after,
+                                                    max_return,
+                                                    c->pc,
+                                                    objects,
+                                                    interval);
+}
+
+int librados::Rados::get_inconsistent_snapsets(const PlacementGroup& pg,
+                                              const object_id_t &start_after,
+                                              unsigned max_return,
+                                              AioCompletion *c,
+                                              std::vector<inconsistent_snapset_t>* snapsets,
+                                              uint32_t* interval)
+{
+  IoCtx ioctx;
+  const pg_t pgid = pg.impl->pgid;
+  int r = ioctx_create2(pgid.pool(), ioctx);
+  if (r < 0) {
+    return r;
+  }
+
+  return ioctx.io_ctx_impl->get_inconsistent_snapsets(pgid,
+                                                     start_after,
+                                                     max_return,
+                                                     c->pc,
+                                                     snapsets,
+                                                     interval);
+}
+
+int librados::Rados::wait_for_latest_osdmap()
+{
+  return client->wait_for_latest_osdmap();
+}
+
+int librados::Rados::blacklist_add(const std::string& client_address,
+                                  uint32_t expire_seconds)
+{
+  return client->blacklist_add(client_address, expire_seconds);
+}
+
+librados::PoolAsyncCompletion *librados::Rados::pool_async_create_completion()
+{
+  PoolAsyncCompletionImpl *c = new PoolAsyncCompletionImpl;
+  return new PoolAsyncCompletion(c);
+}
+
+librados::AioCompletion *librados::Rados::aio_create_completion()
+{
+  AioCompletionImpl *c = new AioCompletionImpl;
+  return new AioCompletion(c);
+}
+
+librados::AioCompletion *librados::Rados::aio_create_completion(void *cb_arg,
+                                                               callback_t cb_complete,
+                                                               callback_t cb_safe)
+{
+  AioCompletionImpl *c;
+  int r = rados_aio_create_completion(cb_arg, cb_complete, cb_safe, (void**)&c);
+  ceph_assert(r == 0);
+  return new AioCompletion(c);
+}
+
+librados::ObjectOperation::ObjectOperation()
+{
+  impl = new ObjectOperationImpl;
+}
+
+librados::ObjectOperation::~ObjectOperation()
+{
+  delete impl;
+}
+
+///////////////////////////// ListObject //////////////////////////////
+librados::ListObject::ListObject() : impl(NULL)
+{
+}
+
+librados::ListObject::ListObject(librados::ListObjectImpl *i): impl(i)
+{
+}
+
+librados::ListObject::ListObject(const ListObject& rhs)
+{
+  if (rhs.impl == NULL) {
+    impl = NULL;
+    return;
+  }
+  impl = new ListObjectImpl();
+  *impl = *(rhs.impl);
+}
+
+librados::ListObject& librados::ListObject::operator=(const ListObject& rhs)
+{
+  if (rhs.impl == NULL) {
+    delete impl;
+    impl = NULL;
+    return *this;
+  }
+  if (impl == NULL)
+    impl = new ListObjectImpl();
+  *impl = *(rhs.impl);
+  return *this;
+}
+
+librados::ListObject::~ListObject()
+{
+  if (impl)
+    delete impl;
+  impl = NULL;
+}
+
+const std::string& librados::ListObject::get_nspace() const
+{
+  return impl->get_nspace();
+}
+
+const std::string& librados::ListObject::get_oid() const
+{
+  return impl->get_oid();
+}
+
+const std::string& librados::ListObject::get_locator() const
+{
+  return impl->get_locator();
+}
+
+std::ostream& librados::operator<<(std::ostream& out, const librados::ListObject& lop)
+{
+  out << *(lop.impl);
+  return out;
+}
+
+librados::ObjectCursor::ObjectCursor()
+{
+  c_cursor = (rados_object_list_cursor)new hobject_t();
+}
+
+librados::ObjectCursor::~ObjectCursor()
+{
+  hobject_t *h = (hobject_t *)c_cursor;
+  delete h;
+}
+
+librados::ObjectCursor::ObjectCursor(rados_object_list_cursor c)
+{
+  if (!c) {
+    c_cursor = nullptr;
+  } else {
+    c_cursor = (rados_object_list_cursor)new hobject_t(*(hobject_t *)c);
+  }
+}
+
+librados::ObjectCursor& librados::ObjectCursor::operator=(const librados::ObjectCursor& rhs)
+{
+  if (rhs.c_cursor != nullptr) {
+    hobject_t *h = (hobject_t*)rhs.c_cursor;
+    c_cursor = (rados_object_list_cursor)(new hobject_t(*h));
+  } else {
+    c_cursor = nullptr;
+  }
+  return *this;
+}
+
+bool librados::ObjectCursor::operator<(const librados::ObjectCursor &rhs) const
+{
+  const hobject_t lhs_hobj = (c_cursor == nullptr) ? hobject_t() : *((hobject_t*)c_cursor);
+  const hobject_t rhs_hobj = (rhs.c_cursor == nullptr) ? hobject_t() : *((hobject_t*)(rhs.c_cursor));
+  return lhs_hobj < rhs_hobj;
+}
+
+bool librados::ObjectCursor::operator==(const librados::ObjectCursor &rhs) const
+{
+  const hobject_t lhs_hobj = (c_cursor == nullptr) ? hobject_t() : *((hobject_t*)c_cursor);
+  const hobject_t rhs_hobj = (rhs.c_cursor == nullptr) ? hobject_t() : *((hobject_t*)(rhs.c_cursor));
+  return cmp(lhs_hobj, rhs_hobj) == 0;
+}
+librados::ObjectCursor::ObjectCursor(const librados::ObjectCursor &rhs)
+{
+  *this = rhs;
+}
+
+librados::ObjectCursor librados::IoCtx::object_list_begin()
+{
+  hobject_t *h = new hobject_t(io_ctx_impl->objecter->enumerate_objects_begin());
+  ObjectCursor oc;
+  oc.set((rados_object_list_cursor)h);
+  return oc;
+}
+
+
+librados::ObjectCursor librados::IoCtx::object_list_end()
+{
+  hobject_t *h = new hobject_t(io_ctx_impl->objecter->enumerate_objects_end());
+  librados::ObjectCursor oc;
+  oc.set((rados_object_list_cursor)h);
+  return oc;
+}
+
+
+void librados::ObjectCursor::set(rados_object_list_cursor c)
+{
+  delete (hobject_t*)c_cursor;
+  c_cursor = c;
+}
+
+string librados::ObjectCursor::to_str() const
+{
+  stringstream ss;
+  ss << *(hobject_t *)c_cursor;
+  return ss.str();
+}
+
+bool librados::ObjectCursor::from_str(const string& s)
+{
+  if (s.empty()) {
+    *(hobject_t *)c_cursor = hobject_t();
+    return true;
+  }
+  return ((hobject_t *)c_cursor)->parse(s);
+}
+
+CEPH_RADOS_API std::ostream& librados::operator<<(std::ostream& os, const librados::ObjectCursor& oc)
+{
+  if (oc.c_cursor) {
+    os << *(hobject_t *)oc.c_cursor;
+  } else {
+    os << hobject_t();
+  }
+  return os;
+}
+
+bool librados::IoCtx::object_list_is_end(const ObjectCursor &oc)
+{
+  hobject_t *h = (hobject_t *)oc.c_cursor;
+  return h->is_max();
+}
+
+int librados::IoCtx::object_list(const ObjectCursor &start,
+                const ObjectCursor &finish,
+                const size_t result_item_count,
+                const bufferlist &filter,
+                std::vector<ObjectItem> *result,
+                ObjectCursor *next)
+{
+  ceph_assert(result != nullptr);
+  ceph_assert(next != nullptr);
+  result->clear();
+
+  C_SaferCond cond;
+  hobject_t next_hash;
+  std::list<librados::ListObjectImpl> obj_result;
+  io_ctx_impl->objecter->enumerate_objects(
+      io_ctx_impl->poolid,
+      io_ctx_impl->oloc.nspace,
+      *((hobject_t*)start.c_cursor),
+      *((hobject_t*)finish.c_cursor),
+      result_item_count,
+      filter,
+      &obj_result,
+      &next_hash,
+      &cond);
+
+  int r = cond.wait();
+  if (r < 0) {
+    next->set((rados_object_list_cursor)(new hobject_t(hobject_t::get_max())));
+    return r;
+  }
+
+  next->set((rados_object_list_cursor)(new hobject_t(next_hash)));
+
+  for (std::list<librados::ListObjectImpl>::iterator i = obj_result.begin();
+       i != obj_result.end(); ++i) {
+    ObjectItem oi;
+    oi.oid = i->oid;
+    oi.nspace = i->nspace;
+    oi.locator = i->locator;
+    result->push_back(oi);
+  }
+
+  return obj_result.size();
+}
+
+void librados::IoCtx::object_list_slice(
+    const ObjectCursor start,
+    const ObjectCursor finish,
+    const size_t n,
+    const size_t m,
+    ObjectCursor *split_start,
+    ObjectCursor *split_finish)
+{
+  ceph_assert(split_start != nullptr);
+  ceph_assert(split_finish != nullptr);
+
+  io_ctx_impl->object_list_slice(
+      *((hobject_t*)(start.c_cursor)),
+      *((hobject_t*)(finish.c_cursor)),
+      n,
+      m,
+      (hobject_t*)(split_start->c_cursor),
+      (hobject_t*)(split_finish->c_cursor));
+}
+
+int librados::IoCtx::application_enable(const std::string& app_name,
+                                        bool force)
+{
+  return io_ctx_impl->application_enable(app_name, force);
+}
+
+int librados::IoCtx::application_enable_async(const std::string& app_name,
+                                              bool force,
+                                              PoolAsyncCompletion *c)
+{
+  io_ctx_impl->application_enable_async(app_name, force, c->pc);
+  return 0;
+}
+
+int librados::IoCtx::application_list(std::set<std::string> *app_names)
+{
+  return io_ctx_impl->application_list(app_names);
+}
+
+int librados::IoCtx::application_metadata_get(const std::string& app_name,
+                                              const std::string &key,
+                                              std::string* value)
+{
+  return io_ctx_impl->application_metadata_get(app_name, key, value);
+}
+
+int librados::IoCtx::application_metadata_set(const std::string& app_name,
+                                              const std::string &key,
+                                              const std::string& value)
+{
+  return io_ctx_impl->application_metadata_set(app_name, key, value);
+}
+
+int librados::IoCtx::application_metadata_remove(const std::string& app_name,
+                                                 const std::string &key)
+{
+  return io_ctx_impl->application_metadata_remove(app_name, key);
+}
+
+int librados::IoCtx::application_metadata_list(const std::string& app_name,
+                                               std::map<std::string, std::string> *values)
+{
+  return io_ctx_impl->application_metadata_list(app_name, values);
+}
diff --git a/src/librados/librados_tp.cc b/src/librados/librados_tp.cc
new file mode 100644 (file)
index 0000000..b696de8
--- /dev/null
@@ -0,0 +1,9 @@
+#include "acconfig.h"
+
+#ifdef WITH_LTTNG
+#define TRACEPOINT_DEFINE
+#define TRACEPOINT_PROBE_DYNAMIC_LINKAGE
+#include "tracing/librados.h"
+#undef TRACEPOINT_PROBE_DYNAMIC_LINKAGE
+#undef TRACEPOINT_DEFINE
+#endif
diff --git a/src/librados/librados_util.cc b/src/librados/librados_util.cc
new file mode 100644 (file)
index 0000000..109bf9a
--- /dev/null
@@ -0,0 +1,61 @@
+#include "librados_util.h"
+
+uint8_t get_checksum_op_type(rados_checksum_type_t type) {
+  switch (type) {
+  case LIBRADOS_CHECKSUM_TYPE_XXHASH32:
+    return CEPH_OSD_CHECKSUM_OP_TYPE_XXHASH32;
+  case LIBRADOS_CHECKSUM_TYPE_XXHASH64:
+    return CEPH_OSD_CHECKSUM_OP_TYPE_XXHASH64;
+  case LIBRADOS_CHECKSUM_TYPE_CRC32C:
+    return CEPH_OSD_CHECKSUM_OP_TYPE_CRC32C;
+  default:
+    return -1;
+  }
+}
+
+int get_op_flags(int flags)
+{
+  int rados_flags = 0;
+  if (flags & LIBRADOS_OP_FLAG_EXCL)
+    rados_flags |= CEPH_OSD_OP_FLAG_EXCL;
+  if (flags & LIBRADOS_OP_FLAG_FAILOK)
+    rados_flags |= CEPH_OSD_OP_FLAG_FAILOK;
+  if (flags & LIBRADOS_OP_FLAG_FADVISE_RANDOM)
+    rados_flags |= CEPH_OSD_OP_FLAG_FADVISE_RANDOM;
+  if (flags & LIBRADOS_OP_FLAG_FADVISE_SEQUENTIAL)
+    rados_flags |= CEPH_OSD_OP_FLAG_FADVISE_SEQUENTIAL;
+  if (flags & LIBRADOS_OP_FLAG_FADVISE_WILLNEED)
+    rados_flags |= CEPH_OSD_OP_FLAG_FADVISE_WILLNEED;
+  if (flags & LIBRADOS_OP_FLAG_FADVISE_DONTNEED)
+    rados_flags |= CEPH_OSD_OP_FLAG_FADVISE_DONTNEED;
+  if (flags & LIBRADOS_OP_FLAG_FADVISE_NOCACHE)
+    rados_flags |= CEPH_OSD_OP_FLAG_FADVISE_NOCACHE;
+  return rados_flags;
+}
+
+int translate_flags(int flags)
+{
+  int op_flags = 0;
+  if (flags & librados::OPERATION_BALANCE_READS)
+    op_flags |= CEPH_OSD_FLAG_BALANCE_READS;
+  if (flags & librados::OPERATION_LOCALIZE_READS)
+    op_flags |= CEPH_OSD_FLAG_LOCALIZE_READS;
+  if (flags & librados::OPERATION_ORDER_READS_WRITES)
+    op_flags |= CEPH_OSD_FLAG_RWORDERED;
+  if (flags & librados::OPERATION_IGNORE_CACHE)
+    op_flags |= CEPH_OSD_FLAG_IGNORE_CACHE;
+  if (flags & librados::OPERATION_SKIPRWLOCKS)
+    op_flags |= CEPH_OSD_FLAG_SKIPRWLOCKS;
+  if (flags & librados::OPERATION_IGNORE_OVERLAY)
+    op_flags |= CEPH_OSD_FLAG_IGNORE_OVERLAY;
+  if (flags & librados::OPERATION_FULL_TRY)
+    op_flags |= CEPH_OSD_FLAG_FULL_TRY;
+  if (flags & librados::OPERATION_FULL_FORCE)
+    op_flags |= CEPH_OSD_FLAG_FULL_FORCE;
+  if (flags & librados::OPERATION_IGNORE_REDIRECT)
+    op_flags |= CEPH_OSD_FLAG_IGNORE_REDIRECT;
+  if (flags & librados::OPERATION_ORDERSNAP)
+    op_flags |= CEPH_OSD_FLAG_ORDERSNAP;
+
+  return op_flags;
+}
diff --git a/src/librados/librados_util.h b/src/librados/librados_util.h
new file mode 100644 (file)
index 0000000..ab9c461
--- /dev/null
@@ -0,0 +1,34 @@
+#include <cstdint>
+#include "acconfig.h"
+#include "include/rados/librados.h"
+#include "IoCtxImpl.h"
+
+#ifdef WITH_LTTNG
+#include "tracing/librados.h"
+#else
+#define tracepoint(...)
+#endif
+
+uint8_t get_checksum_op_type(rados_checksum_type_t type);
+int get_op_flags(int flags);
+int translate_flags(int flags);
+
+struct librados::ObjListCtx {
+  librados::IoCtxImpl dupctx;
+  librados::IoCtxImpl *ctx;
+  Objecter::NListContext *nlc;
+  bool legacy_list_api;
+
+  ObjListCtx(IoCtxImpl *c, Objecter::NListContext *nl, bool legacy=false)
+    : nlc(nl),
+      legacy_list_api(legacy) {
+    // Get our own private IoCtxImpl so that namespace setting isn't
+    // changed by caller between uses.
+    ctx = &dupctx;
+    dupctx.dup(*c);
+  }
+  ~ObjListCtx() {
+    ctx = NULL;
+    delete nlc;
+  }
+};
index 5b2dd7566aff59aac04d57711ac6b1b564ba52e6..d9d022f675f3e68841c5179386e2ee1c394cf56c 100644 (file)
@@ -3,10 +3,12 @@ set(libradosstriper_srcs
   RadosStriperImpl.cc
   MultiAioCompletionImpl.cc)
 add_library(radosstriper ${CEPH_SHARED}
-  ${libradosstriper_srcs}
-  $<TARGET_OBJECTS:librados_objs>)
+  ${libradosstriper_srcs})
 target_link_libraries(radosstriper
-  PRIVATE librados cls_lock_client osdc ceph-common pthread ${CRYPTO_LIBS} ${EXTRALIBS})
+  PRIVATE
+    librados-cxx
+    librados_impl cls_lock_client osdc ceph-common
+    pthread ${CRYPTO_LIBS} ${EXTRALIBS})
 set_target_properties(radosstriper PROPERTIES
   OUPUT_NAME radosstriper
   VERSION 1.0.0
index 2ba963ed0db0f5a2d40e60956ecc8eabb6afe21b..5501dc633c24190e09eaab6fdbc1cdf3b0f87b4d 100644 (file)
@@ -153,7 +153,7 @@ target_link_libraries(librbd PRIVATE
   rbd_internal
   rbd_types
   journal
-  librados 
+  librados-cxx
   cls_rbd_client 
   cls_lock_client 
   cls_journal_client 
index e9d3804c5f57a3a282ca1903b4cc1b86c61c28ab..5ffae423be463f91ef04a789d7056d29da0e87fe 100644 (file)
@@ -10,7 +10,7 @@ set(librbd_replay_srcs
     rbd_loc.cc
     Replayer.cc)
 add_library(rbd_replay STATIC ${librbd_replay_srcs})
-target_link_libraries(rbd_replay PRIVATE librbd librados global)
+target_link_libraries(rbd_replay PRIVATE librbd librados-cxx global)
 
 add_executable(rbd-replay
   rbd-replay.cc)
index 4d488a0eba34449181be4324f7ab8a9ccefb9db7..939282733c221d698617d75df02b8a9974c92f31 100644 (file)
@@ -169,7 +169,7 @@ add_dependencies(rgw_a civetweb_h)
 target_include_directories(rgw_a SYSTEM PUBLIC "../rapidjson/include")
 
 target_link_libraries(rgw_a PRIVATE
-  librados cls_otp_client cls_lock_client cls_rgw_client cls_refcount_client
+  librados-cxx cls_otp_client cls_lock_client cls_rgw_client cls_refcount_client
   cls_log_client cls_timeindex_client cls_version_client
   cls_user_client ceph-common common_utf8 global
   ${CURL_LIBRARIES}
@@ -296,7 +296,7 @@ add_library(rgw_admin_user SHARED
 add_dependencies(rgw_admin_user civetweb_h)
 
 target_link_libraries(rgw_admin_user PRIVATE
-  librados
+  librados-cxx
   cls_rgw_client
   cls_otp_client
   cls_lock_client
index 43243785864ffc32b0b1149dc6a8eea23bf4ffe5..f9eb45b5bd8c7071a729c893dd844ed3bc7eaf28 100644 (file)
@@ -130,7 +130,7 @@ target_link_libraries(ceph_bench_log global pthread rt ${BLKID_LIBRARIES} ${CMAK
 add_executable(ceph_test_mutate
   test_mutate.cc
   )
-target_link_libraries(ceph_test_mutate global librados ${BLKID_LIBRARIES}
+target_link_libraries(ceph_test_mutate global librados-cxx ${BLKID_LIBRARIES} 
   ${CMAKE_DL_LIBS})
 
 # test_trans
@@ -150,7 +150,7 @@ add_executable(ceph_omapbench
   ${omapbench_srcs}
   )
 target_link_libraries(ceph_omapbench
-  librados
+  librados-cxx
   Boost::program_options
   global
   ${BLKID_LIBRARIES}
@@ -164,7 +164,7 @@ if(WITH_KVS)
     ${CMAKE_SOURCE_DIR}/src/key_value_store/kv_flat_btree_async.cc
     )
   add_executable(ceph_kvstorebench ${kvstorebench_srcs})
-  target_link_libraries(ceph_kvstorebench librados global ${BLKID_LIBRARIES} ${CMAKE_DL_LIBS})
+  target_link_libraries(ceph_kvstorebench librados-cxx global ${BLKID_LIBRARIES} ${CMAKE_DL_LIBS})
   install(TARGETS ceph_kvstorebench DESTINATION bin)
 endif(WITH_KVS)
 
@@ -356,7 +356,7 @@ endif(${WITH_RADOSGW})
 add_executable(ceph_multi_stress_watch
   multi_stress_watch.cc
   )
-target_link_libraries(ceph_multi_stress_watch librados global radostest
+target_link_libraries(ceph_multi_stress_watch librados-cxx global radostest-cxx
   ${BLKID_LIBRARIES} ${CMAKE_DL_LIBS})
 
 #ceph_perf_local
@@ -418,9 +418,9 @@ add_executable(ceph_test_stress_watch
   test_stress_watch.cc
   )
 target_link_libraries(ceph_test_stress_watch
-  librados
+  librados-cxx
   ${UNITTEST_LIBS}
-  radostest
+  radostest-cxx
   ${EXTRALIBS}
   ${BLKID_LIBRARIES}
   ${CMAKE_DL_LIBS}
index 4ed22132f269bf69ed60e3727929bb0705ac9b92..1363d746b6a1bcd576804880e6f97ff24b0996b1 100644 (file)
@@ -2,12 +2,12 @@ add_executable(ceph_test_cls_hello
   test_cls_hello.cc
   )
 target_link_libraries(ceph_test_cls_hello
-  librados
+  librados-cxx
   global
   ${EXTRALIBS}
   ${BLKID_LIBRARIES}
   ${CMAKE_DL_LIBS}
-  radostest
+  radostest-cxx
   ${UNITTEST_LIBS}
   )
 install(TARGETS
index 55386a86fb227d7ee676b78a1433e0eb97e46cc0..7cf30125f0b3e98b25001654b91abc82a1f404f2 100644 (file)
@@ -17,7 +17,7 @@
 
 #include "include/rados/librados.hpp"
 #include "include/encoding.h"
-#include "test/librados/test.h"
+#include "test/librados/test_cxx.h"
 #include "gtest/gtest.h"
 
 using namespace librados;
index 3f2cad9c01f64204f45ecb6b423376192716cbcd..08d47655a79852b83e8112410b984f57c611fa5d 100644 (file)
@@ -4,13 +4,13 @@ add_executable(ceph_test_cls_journal
   $<TARGET_OBJECTS:common_texttable_obj>)
 target_link_libraries(ceph_test_cls_journal
   cls_journal_client
-  librados
+  librados-cxx
   global
   ${UNITTEST_LIBS}
   ${CMAKE_DL_LIBS}
   ${CRYPTO_LIBS}
   ${EXTRALIBS}
-  radostest)
+  radostest-cxx)
 install(TARGETS
   ceph_test_cls_journal
   DESTINATION ${CMAKE_INSTALL_BINDIR})
index 9df173d6202714aded8c22acb95a2ac5ee1d8a50..2082bbc2935c3158d66912a1bd09d456d68d5e28 100644 (file)
@@ -4,7 +4,7 @@
 #include "cls/journal/cls_journal_client.h"
 #include "include/stringify.h"
 #include "common/Cond.h"
-#include "test/librados/test.h"
+#include "test/librados/test_cxx.h"
 #include "gtest/gtest.h"
 #include <errno.h>
 #include <set>
index 49214e17e68c396d06f430ac1628a22a79e6e05d..53f36725c56c12eb32acdb091729bc473be0d0cf 100644 (file)
@@ -3,14 +3,14 @@ add_executable(ceph_test_cls_lock
   )
 target_link_libraries(ceph_test_cls_lock
   cls_lock_client
-  librados
+  librados-cxx
   global
   ${UNITTEST_LIBS}
   ${BLKID_LIBRARIES}
   ${CMAKE_DL_LIBS}
   ${CRYPTO_LIBS}
   ${EXTRALIBS}
-  radostest
+  radostest-cxx
   )
 install(TARGETS
   ceph_test_cls_lock
index 37d10a19cbcf4f0b5914e568422d76e7d62c7c03..c9bc2dd4b89b321111ab0bb0425dd8d0b58966c0 100644 (file)
@@ -20,7 +20,7 @@
 #include "msg/msg_types.h"
 #include "include/rados/librados.hpp"
 
-#include "test/librados/test.h"
+#include "test/librados/test_cxx.h"
 #include "gtest/gtest.h"
 
 using namespace librados;
index acbd31d6bd908a3f25e7f0b6ed1f6593e6c0b006..c27d9843a1c804b84b31b5918d7e521289c8bf3b 100644 (file)
@@ -1,10 +1,10 @@
 add_executable(ceph_test_cls_log
   test_cls_log.cc)
 target_link_libraries(ceph_test_cls_log
-  librados
+  librados-cxx
   cls_log_client
   global
-  radostest
+  radostest-cxx
   ${UNITTEST_LIBS}
   ${BLKID_LIBRARIES}
   ${CMAKE_DL_LIBS}
index daf1ceeb9e9c6591d511adb4a838fc5ba106f62d..05485035ae02d8b115dd830f3cb2d576052329da 100644 (file)
@@ -10,7 +10,7 @@
 #include "global/global_context.h"
 
 #include "gtest/gtest.h"
-#include "test/librados/test.h"
+#include "test/librados/test_cxx.h"
 
 #include <errno.h>
 #include <string>
index 1bc8c4cce6ee851b942537cad41d5c6cc2a73ae2..cfb6ba1171b3e16b0f433858ac2f50ee38af3811 100644 (file)
@@ -4,13 +4,12 @@ add_executable(ceph_test_cls_lua
 target_link_libraries(ceph_test_cls_lua
   cls_lua_client
   liblua
-  librados
+  librados-cxx
   global
   ${UNITTEST_LIBS}
   ${EXTRALIBS}
   ${CMAKE_DL_LIBS}
-  radostest
-)
+  radostest-cxx)
 install(TARGETS
   ceph_test_cls_lua
   DESTINATION ${CMAKE_INSTALL_BINDIR})
index 1cd3a00d2faa9133f6d5b4c6b8585334e3c81686..b67278266592e752bd4491f20f2d1ace87d3bd69 100644 (file)
@@ -1,9 +1,9 @@
 #include <errno.h>
 #include <lua.hpp>
 #include "include/types.h"
-#include "include/rados/librados.h"
+#include "include/rados/librados.hpp"
 #include "gtest/gtest.h"
-#include "test/librados/test.h"
+#include "test/librados/test_cxx.h"
 #include "cls/lua/cls_lua_client.h"
 #include "cls/lua/cls_lua.h"
 
index b52d7f9894a509a71a527eada741b016b24992ad..00108991d3909ff9d92669f02abf50a386c1ce26 100644 (file)
@@ -2,13 +2,13 @@
 add_executable(ceph_test_cls_numops 
   test_cls_numops.cc)
 target_link_libraries(ceph_test_cls_numops 
-  librados 
+  librados-cxx
   global
   cls_numops_client 
   ${EXTRALIBS}
   ${BLKID_LIBRARIES}
   ${CMAKE_DL_LIBS}
-  radostest
+  radostest-cxx
   ${UNITTEST_LIBS} 
   )
 install(TARGETS
index 844caf993cf1d8fde173db4711c2c154bd6597c3..ebb2256208b605aec3a8e0c5d632a1f993f50f7c 100644 (file)
@@ -21,7 +21,7 @@
 #include "cls/numops/cls_numops_client.h"
 #include "gtest/gtest.h"
 #include "include/rados/librados.hpp"
-#include "test/librados/test.h"
+#include "test/librados/test_cxx.h"
 
 using namespace librados;
 
index 77596dd690b548b6effcfa69c15e83dbbeb89b0a..d42deb08848bb429d046efa336827afb749b856e 100644 (file)
@@ -5,13 +5,13 @@ add_executable(ceph_test_cls_rbd
 target_link_libraries(ceph_test_cls_rbd
   cls_rbd_client
   cls_lock_client
-  librados
+  librados-cxx
   global
   ${UNITTEST_LIBS}
   ${CMAKE_DL_LIBS}
   ${CRYPTO_LIBS}
   ${EXTRALIBS}
-  radostest)
+  radostest-cxx)
 install(TARGETS
   ceph_test_cls_rbd
   DESTINATION ${CMAKE_INSTALL_BINDIR})
index 99a0a55b9816e9473c4e273cd4f8d9e69d865bd6..3490dad16074071a0ed940a5c4ef23e6483af128 100644 (file)
@@ -17,7 +17,7 @@
 #include "librbd/Types.h"
 
 #include "gtest/gtest.h"
-#include "test/librados/test.h"
+#include "test/librados/test_cxx.h"
 
 #include <errno.h>
 #include <string>
index 9983828456f4c1cd66194ea0721e3e4917cdfe9a..e9aaeaa6a25d01989d7843513573c187b97e111c 100644 (file)
@@ -3,7 +3,7 @@ add_executable(ceph_test_cls_refcount
   test_cls_refcount.cc
   )
 target_link_libraries(ceph_test_cls_refcount
-  librados
+  librados-cxx
   cls_refcount_client
   global
   ${UNITTEST_LIBS}
@@ -11,7 +11,7 @@ target_link_libraries(ceph_test_cls_refcount
   ${CMAKE_DL_LIBS}
   ${CRYPTO_LIBS}
   ${EXTRALIBS}
-  radostest
+  radostest-cxx
   )
 install(TARGETS
   ceph_test_cls_refcount
index 73ba1db13b3f0782ebe304633b113a240083848a..b481ca0f240b6003dd7b5d16531132969d4c9067 100644 (file)
@@ -5,7 +5,7 @@
 #include "cls/refcount/cls_refcount_client.h"
 
 #include "gtest/gtest.h"
-#include "test/librados/test.h"
+#include "test/librados/test_cxx.h"
 
 #include <errno.h>
 #include <string>
index ca53e9dfc7d22472242d7ba09e34a535544c0040..79409db53b7eb46ff547993fa4838116efd0e82f 100644 (file)
@@ -4,13 +4,13 @@ if(${WITH_RADOSGW})
     )
   target_link_libraries(ceph_test_cls_rgw
     cls_rgw_client
-    librados
+    librados-cxx
     global
     ${UNITTEST_LIBS}
     ${EXTRALIBS}
     ${BLKID_LIBRARIES}
     ${CMAKE_DL_LIBS}
-    radostest)
+    radostest-cxx)
   install(TARGETS
     ceph_test_cls_rgw
     DESTINATION ${CMAKE_INSTALL_BINDIR})
index d09e867ec757dbd0560fec27a7d78ecc40ac7bad..7e78acf8a1c83f4c230dee5bfb4f5e7fbc974509 100644 (file)
@@ -6,7 +6,7 @@
 #include "cls/rgw/cls_rgw_ops.h"
 
 #include "gtest/gtest.h"
-#include "test/librados/test.h"
+#include "test/librados/test_cxx.h"
 #include "global/global_context.h"
 
 #include <errno.h>
index d75971276c206f2526bcfcfcd9a47eea15bfcfe4..cd05900d56f56479138626f8f15c5b1aa11bc5e5 100644 (file)
@@ -2,12 +2,12 @@ add_executable(ceph_test_cls_sdk
   test_cls_sdk.cc
   )
 target_link_libraries(ceph_test_cls_sdk
-  librados
+  librados-cxx
   global
   ${EXTRALIBS}
   ${BLKID_LIBRARIES}
   ${CMAKE_DL_LIBS}
-  radostest
+  radostest-cxx
   ${UNITTEST_LIBS}
   )
 install(TARGETS
index af3452bb5c6cfbb34f72af79262aa027c92d2ddb..44b32196b3aacb4243c8eb57660d60f11b7bbf40 100644 (file)
@@ -1,7 +1,7 @@
 #include <iostream>
 #include <errno.h>
 
-#include "test/librados/test.h"
+#include "test/librados/test_cxx.h"
 #include "gtest/gtest.h"
 
 using namespace librados;
index 7334ce43624d56c673a8359be3c3cd21b7083fe4..d8e43878d6987442e49697681eb94a25e68d2776 100644 (file)
@@ -3,7 +3,7 @@ add_executable(ceph_test_cls_version
   test_cls_version.cc
   )
 target_link_libraries(ceph_test_cls_version
-  librados
+  librados-cxx
   cls_version_client
   global
   ${UNITTEST_LIBS}
@@ -11,6 +11,6 @@ target_link_libraries(ceph_test_cls_version
   ${CMAKE_DL_LIBS}
   ${CRYPTO_LIBS}
   ${EXTRALIBS}
-  radostest
+  radostest-cxx
   )
 
index 4c2d59500df9b29a5e8d4b88a5c690e4b16bd391..9ecea1f2cd29d06840699eb88cec1323f4ab34c6 100644 (file)
@@ -1,12 +1,14 @@
 // -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
 // vim: ts=8 sw=2 smarttab
 
+#include "include/rados/librados.hpp"
 #include "include/types.h"
+
 #include "cls/version/cls_version_types.h"
 #include "cls/version/cls_version_client.h"
 
 #include "gtest/gtest.h"
-#include "test/librados/test.h"
+#include "test/librados/test_cxx.h"
 
 #include <errno.h>
 #include <string>
index 213630e941c81d88f5544045ffac803ef451b2cb..99e0f8ae6e6933a41ac8b3ad742075bc47935dd1 100644 (file)
@@ -27,6 +27,6 @@ target_link_libraries(unittest_journal
   cls_journal_client
   rados_test_stub
   librados
-  radostest
+  radostest-cxx
   global 
   )
index 1c4ed84075ed1f16d8ec5e8d8918f2d1ed941f39..61a9967f75f64f0aeb024c9ea8c9eaa03f571990 100644 (file)
@@ -1,6 +1,7 @@
 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
 // vim: ts=8 sw=2 smarttab
 
+#include "test/librados/test_cxx.h"
 #include "test/journal/RadosTestFixture.h"
 #include "cls/journal/cls_journal_client.h"
 #include "include/stringify.h"
index 33ac156f9d18c9b52972bd9dcc730c4fc9c1ee8c..1e7ccff92f1c41df4876426d4f39420155f0aa10 100644 (file)
@@ -1,30 +1,43 @@
 # radostest
-add_library(libradostest_obj OBJECT test.cc)
-target_include_directories(libradostest_obj PRIVATE
+add_library(radostest_shared OBJECT test_shared.cc)
+target_include_directories(radostest_shared PRIVATE
   $<TARGET_PROPERTY:GTest::GTest,INTERFACE_INCLUDE_DIRECTORIES>)
-set(libradostest_srcs 
-  test_common.cc
-  TestCase.cc)
+
 add_library(radostest STATIC
-  ${libradostest_srcs}
-  $<TARGET_OBJECTS:libradostest_obj>)
-target_link_libraries(radostest
+  test_common.cc
+  TestCase.cc
+  test.cc
+  $<TARGET_OBJECTS:radostest_shared>)
+target_link_libraries(radostest PUBLIC
   GTest::GTest
-  librados
   ceph-common
   json_spirit
   ${EXTRALIBS})
-
+add_library(radostest-cxx STATIC
+  testcase_cxx.cc
+  test_cxx.cc
+  $<TARGET_OBJECTS:radostest_shared>)
+target_link_libraries(radostest-cxx PUBLIC
+  GTest::GTest
+  ceph-common)
 
 add_executable(ceph_test_rados_api_cmd
   cmd.cc)
 target_link_libraries(ceph_test_rados_api_cmd
   librados ${UNITTEST_LIBS} radostest)
+add_executable(ceph_test_rados_api_cmd_pp
+  cmd_cxx.cc)
+target_link_libraries(ceph_test_rados_api_cmd_pp
+  librados-cxx ${UNITTEST_LIBS} radostest-cxx)
 
 add_executable(ceph_test_rados_api_io
   io.cc)
 target_link_libraries(ceph_test_rados_api_io
   librados ${UNITTEST_LIBS} radostest)
+add_executable(ceph_test_rados_api_io_pp
+  io_cxx.cc)
+target_link_libraries(ceph_test_rados_api_io_pp
+  librados-cxx ${UNITTEST_LIBS} radostest-cxx)
 
 add_executable(ceph_test_rados_api_c_write_operations
   c_write_operations.cc)
@@ -40,10 +53,14 @@ add_executable(ceph_test_rados_api_aio
   aio.cc)
 target_link_libraries(ceph_test_rados_api_aio
   librados ${UNITTEST_LIBS} radostest)
+add_executable(ceph_test_rados_api_aio_pp
+  aio_cxx.cc)
+target_link_libraries(ceph_test_rados_api_aio_pp
+  librados-cxx ${UNITTEST_LIBS} radostest-cxx)
 
 add_executable(ceph_test_rados_api_asio asio.cc)
 target_link_libraries(ceph_test_rados_api_asio global
-  librados ${UNITTEST_LIBS})
+  librados-cxx ${UNITTEST_LIBS})
 if(WITH_BOOST_CONTEXT)
   target_link_libraries(ceph_test_rados_api_asio Boost::coroutine Boost::context)
 endif()
@@ -63,60 +80,94 @@ add_executable(ceph_test_rados_api_stat
   stat.cc)
 target_link_libraries(ceph_test_rados_api_stat
   librados ${UNITTEST_LIBS} radostest)
+add_executable(ceph_test_rados_api_stat_pp
+  stat_cxx.cc)
+target_link_libraries(ceph_test_rados_api_stat_pp
+  librados-cxx ${UNITTEST_LIBS} radostest-cxx)
 
 add_executable(ceph_test_rados_api_watch_notify
   watch_notify.cc)
 target_link_libraries(ceph_test_rados_api_watch_notify
   librados ${UNITTEST_LIBS} radostest)
+add_executable(ceph_test_rados_api_watch_notify_pp
+  watch_notify_cxx.cc)
+target_link_libraries(ceph_test_rados_api_watch_notify_pp
+  librados-cxx ${UNITTEST_LIBS} radostest-cxx)
 
 add_executable(ceph_test_rados_api_cls
   cls.cc)
 target_link_libraries(ceph_test_rados_api_cls
-  librados ${UNITTEST_LIBS} radostest)
+  librados-cxx ${UNITTEST_LIBS} radostest-cxx)
 
 add_executable(ceph_test_rados_api_misc
   misc.cc
   $<TARGET_OBJECTS:unit-main>)
 target_link_libraries(ceph_test_rados_api_misc
   librados global ${UNITTEST_LIBS} radostest)
+add_executable(ceph_test_rados_api_misc_pp
+  misc_cxx.cc
+  $<TARGET_OBJECTS:unit-main>)
+target_link_libraries(ceph_test_rados_api_misc_pp
+  librados-cxx global ${UNITTEST_LIBS} radostest-cxx)
 
 add_executable(ceph_test_rados_api_lock
   lock.cc)
 target_link_libraries(ceph_test_rados_api_lock
   librados ${UNITTEST_LIBS} radostest)
+add_executable(ceph_test_rados_api_lock_pp
+  lock_cxx.cc)
+target_link_libraries(ceph_test_rados_api_lock_pp
+  librados-cxx ${UNITTEST_LIBS} radostest-cxx)
 
 add_executable(ceph_test_rados_api_service
   service.cc)
 target_link_libraries(ceph_test_rados_api_service
   librados global ${UNITTEST_LIBS} radostest)
+add_executable(ceph_test_rados_api_service_pp
+  service_cxx.cc)
+target_link_libraries(ceph_test_rados_api_service_pp
+  librados-cxx global ${UNITTEST_LIBS} radostest-cxx)
 
-add_executable(ceph_test_rados_api_tier
-  tier.cc
+add_executable(ceph_test_rados_api_tier_pp
+  tier_cxx.cc
   $<TARGET_OBJECTS:unit-main>)
-target_link_libraries(ceph_test_rados_api_tier
-  librados global ${UNITTEST_LIBS} Boost::system radostest)
+target_link_libraries(ceph_test_rados_api_tier_pp
+  librados-cxx global ${UNITTEST_LIBS} Boost::system radostest-cxx)
 
 add_executable(ceph_test_rados_api_snapshots
   snapshots.cc)
 target_link_libraries(ceph_test_rados_api_snapshots
   librados ${UNITTEST_LIBS} radostest)
+add_executable(ceph_test_rados_api_snapshots_pp
+  snapshots_cxx.cc)
+target_link_libraries(ceph_test_rados_api_snapshots_pp
+  librados-cxx ${UNITTEST_LIBS} radostest-cxx)
 
 install(TARGETS
   ceph_test_rados_api_aio
+  ceph_test_rados_api_aio_pp
   ceph_test_rados_api_asio
   ceph_test_rados_api_c_read_operations
   ceph_test_rados_api_c_write_operations
   ceph_test_rados_api_cmd
+  ceph_test_rados_api_cmd_pp
   ceph_test_rados_api_io
+  ceph_test_rados_api_io_pp
   ceph_test_rados_api_list
   ceph_test_rados_api_lock
+  ceph_test_rados_api_lock_pp
   ceph_test_rados_api_misc
+  ceph_test_rados_api_misc_pp
   ceph_test_rados_api_pool
   ceph_test_rados_api_service
+  ceph_test_rados_api_service_pp
   ceph_test_rados_api_snapshots
+  ceph_test_rados_api_snapshots_pp
   ceph_test_rados_api_stat
-  ceph_test_rados_api_tier
+  ceph_test_rados_api_stat_pp
+  ceph_test_rados_api_tier_pp
   ceph_test_rados_api_watch_notify
+  ceph_test_rados_api_watch_notify_pp
   DESTINATION ${CMAKE_INSTALL_BINDIR})
 
 # unittest_librados
index 37dbd2a923bfc8d7f07a286bdde3e630e9dcb796..1066587b5ec91b2ff2f9e7563d267f55ea1a2416 100644 (file)
@@ -6,24 +6,10 @@
 #include "test/librados/TestCase.h"
 #include "include/scope_guard.h"
 
-using namespace librados;
 
 std::string RadosTestNS::pool_name;
 rados_t RadosTestNS::s_cluster = NULL;
 
-namespace {
-
-void init_rand() {
-  static bool seeded = false;
-  if (!seeded) {
-    seeded = true;
-    int seed = getpid();
-    std::cout << "seed " << seed << std::endl;
-    srand(seed);
-  }
-}
-
-} // anonymous namespace
 
 void RadosTestNS::SetUpTestCase()
 {
@@ -74,131 +60,6 @@ void RadosTestNS::cleanup_all_objects(rados_ioctx_t ioctx)
   }
 }
 
-std::string RadosTestPPNS::pool_name;
-Rados RadosTestPPNS::s_cluster;
-
-void RadosTestPPNS::SetUpTestCase()
-{
-  pool_name = get_temp_pool_name();
-  ASSERT_EQ("", create_one_pool_pp(pool_name, s_cluster));
-}
-
-void RadosTestPPNS::TearDownTestCase()
-{
-  ASSERT_EQ(0, destroy_one_pool_pp(pool_name, s_cluster));
-}
-
-void RadosTestPPNS::SetUp()
-{
-  ASSERT_EQ(0, cluster.ioctx_create(pool_name.c_str(), ioctx));
-  bool requires;
-  ASSERT_EQ(0, ioctx.pool_requires_alignment2(&requires));
-  ASSERT_FALSE(requires);
-}
-
-void RadosTestPPNS::TearDown()
-{
-  if (cleanup)
-    cleanup_all_objects(ioctx);
-  ioctx.close();
-}
-
-void RadosTestPPNS::cleanup_all_objects(librados::IoCtx ioctx)
-{
-  // remove all objects to avoid polluting other tests
-  ioctx.snap_set_read(librados::SNAP_HEAD);
-  ioctx.set_namespace(all_nspaces);
-  for (NObjectIterator it = ioctx.nobjects_begin();
-       it != ioctx.nobjects_end(); ++it) {
-    ioctx.locator_set_key(it->get_locator());
-    ioctx.set_namespace(it->get_nspace());
-    ASSERT_EQ(0, ioctx.remove(it->get_oid()));
-  }
-}
-
-std::string RadosTestParamPPNS::pool_name;
-std::string RadosTestParamPPNS::cache_pool_name;
-Rados RadosTestParamPPNS::s_cluster;
-
-void RadosTestParamPPNS::SetUpTestCase()
-{
-  pool_name = get_temp_pool_name();
-  ASSERT_EQ("", create_one_pool_pp(pool_name, s_cluster));
-}
-
-void RadosTestParamPPNS::TearDownTestCase()
-{
-  if (cache_pool_name.length()) {
-    // tear down tiers
-    bufferlist inbl;
-    ASSERT_EQ(0, s_cluster.mon_command(
-      "{\"prefix\": \"osd tier remove-overlay\", \"pool\": \"" + pool_name +
-      "\"}",
-      inbl, NULL, NULL));
-    ASSERT_EQ(0, s_cluster.mon_command(
-      "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
-      "\", \"tierpool\": \"" + cache_pool_name + "\"}",
-      inbl, NULL, NULL));
-    ASSERT_EQ(0, s_cluster.mon_command(
-      "{\"prefix\": \"osd pool delete\", \"pool\": \"" + cache_pool_name +
-      "\", \"pool2\": \"" + cache_pool_name + "\", \"sure\": \"--yes-i-really-really-mean-it\"}",
-      inbl, NULL, NULL));
-    cache_pool_name = "";
-  }
-  ASSERT_EQ(0, destroy_one_pool_pp(pool_name, s_cluster));
-}
-
-void RadosTestParamPPNS::SetUp()
-{
-  if (strcmp(GetParam(), "cache") == 0 && cache_pool_name.empty()) {
-    cache_pool_name = get_temp_pool_name();
-    bufferlist inbl;
-    ASSERT_EQ(0, cluster.mon_command(
-      "{\"prefix\": \"osd pool create\", \"pool\": \"" + cache_pool_name +
-      "\", \"pg_num\": 4}",
-      inbl, NULL, NULL));
-    ASSERT_EQ(0, cluster.mon_command(
-      "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-      "\", \"tierpool\": \"" + cache_pool_name +
-      "\", \"force_nonempty\": \"--force-nonempty\" }",
-      inbl, NULL, NULL));
-    ASSERT_EQ(0, cluster.mon_command(
-      "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
-      "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
-      inbl, NULL, NULL));
-    ASSERT_EQ(0, cluster.mon_command(
-      "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
-      "\", \"mode\": \"writeback\"}",
-      inbl, NULL, NULL));
-    cluster.wait_for_latest_osdmap();
-  }
-
-  ASSERT_EQ(0, cluster.ioctx_create(pool_name.c_str(), ioctx));
-  bool requires;
-  ASSERT_EQ(0, ioctx.pool_requires_alignment2(&requires));
-  ASSERT_FALSE(requires);
-}
-
-void RadosTestParamPPNS::TearDown()
-{
-  if (cleanup)
-    cleanup_all_objects(ioctx);
-  ioctx.close();
-}
-
-void RadosTestParamPPNS::cleanup_all_objects(librados::IoCtx ioctx)
-{
-  // remove all objects to avoid polluting other tests
-  ioctx.snap_set_read(librados::SNAP_HEAD);
-  ioctx.set_namespace(all_nspaces);
-  for (NObjectIterator it = ioctx.nobjects_begin();
-       it != ioctx.nobjects_end(); ++it) {
-    ioctx.locator_set_key(it->get_locator());
-    ioctx.set_namespace(it->get_nspace());
-    ASSERT_EQ(0, ioctx.remove(it->get_oid()));
-  }
-}
-
 std::string RadosTestECNS::pool_name;
 rados_t RadosTestECNS::s_cluster = NULL;
 
@@ -231,37 +92,6 @@ void RadosTestECNS::TearDown()
   rados_ioctx_destroy(ioctx);
 }
 
-std::string RadosTestECPPNS::pool_name;
-Rados RadosTestECPPNS::s_cluster;
-
-void RadosTestECPPNS::SetUpTestCase()
-{
-  pool_name = get_temp_pool_name();
-  ASSERT_EQ("", create_one_ec_pool_pp(pool_name, s_cluster));
-}
-
-void RadosTestECPPNS::TearDownTestCase()
-{
-  ASSERT_EQ(0, destroy_one_ec_pool_pp(pool_name, s_cluster));
-}
-
-void RadosTestECPPNS::SetUp()
-{
-  ASSERT_EQ(0, cluster.ioctx_create(pool_name.c_str(), ioctx));
-  bool requires;
-  ASSERT_EQ(0, ioctx.pool_requires_alignment2(&requires));
-  ASSERT_TRUE(requires);
-  ASSERT_EQ(0, ioctx.pool_required_alignment2(&alignment));
-  ASSERT_NE(0U, alignment);
-}
-
-void RadosTestECPPNS::TearDown()
-{
-  if (cleanup)
-    cleanup_all_objects(ioctx);
-  ioctx.close();
-}
-
 std::string RadosTest::pool_name;
 rados_t RadosTest::s_cluster = NULL;
 
@@ -322,179 +152,6 @@ void RadosTest::cleanup_namespace(rados_ioctx_t ioctx, std::string ns)
   }
 }
 
-std::string RadosTestPP::pool_name;
-Rados RadosTestPP::s_cluster;
-
-void RadosTestPP::SetUpTestCase()
-{
-  init_rand();
-
-  pool_name = get_temp_pool_name();
-  ASSERT_EQ("", create_one_pool_pp(pool_name, s_cluster));
-}
-
-void RadosTestPP::TearDownTestCase()
-{
-  ASSERT_EQ(0, destroy_one_pool_pp(pool_name, s_cluster));
-}
-
-void RadosTestPP::SetUp()
-{
-  ASSERT_EQ(0, cluster.ioctx_create(pool_name.c_str(), ioctx));
-  nspace = get_temp_pool_name();
-  ioctx.set_namespace(nspace);
-  bool requires;
-  ASSERT_EQ(0, ioctx.pool_requires_alignment2(&requires));
-  ASSERT_FALSE(requires);
-}
-
-void RadosTestPP::TearDown()
-{
-  if (cleanup) {
-    cleanup_default_namespace(ioctx);
-    cleanup_namespace(ioctx, nspace);
-  }
-  ioctx.close();
-}
-
-void RadosTestPP::cleanup_default_namespace(librados::IoCtx ioctx)
-{
-  // remove all objects from the default namespace to avoid polluting
-  // other tests
-  cleanup_namespace(ioctx, "");
-}
-
-void RadosTestPP::cleanup_namespace(librados::IoCtx ioctx, std::string ns)
-{
-  ioctx.snap_set_read(librados::SNAP_HEAD);
-  ioctx.set_namespace(ns);
-  int tries = 600;
-  while (--tries) {
-    int got_enoent = 0;
-    for (NObjectIterator it = ioctx.nobjects_begin();
-        it != ioctx.nobjects_end(); ++it) {
-      ioctx.locator_set_key(it->get_locator());
-      ObjectWriteOperation op;
-      op.remove();
-      librados::AioCompletion *completion = s_cluster.aio_create_completion();
-      auto sg = make_scope_guard([&] { completion->release(); });
-      ASSERT_EQ(0, ioctx.aio_operate(it->get_oid(), completion, &op,
-                                    librados::OPERATION_IGNORE_CACHE));
-      completion->wait_for_safe();
-      if (completion->get_return_value() == -ENOENT) {
-       ++got_enoent;
-       std::cout << " got ENOENT removing " << it->get_oid() << std::endl;
-      } else {
-       ASSERT_EQ(0, completion->get_return_value());
-      }
-    }
-    if (!got_enoent) {
-      break;
-    }
-    std::cout << " got ENOENT on " << got_enoent
-             << " objects, waiting a bit for snap"
-             << " trimming before retrying " << tries << " more times..."
-             << std::endl;
-    sleep(1);
-  }
-  if (tries == 0) {
-    std::cout << "failed to clean up" << std::endl;
-    ASSERT_TRUE(false);
-  }
-}
-
-std::string RadosTestParamPP::pool_name;
-std::string RadosTestParamPP::cache_pool_name;
-Rados RadosTestParamPP::s_cluster;
-
-void RadosTestParamPP::SetUpTestCase()
-{
-  pool_name = get_temp_pool_name();
-  ASSERT_EQ("", create_one_pool_pp(pool_name, s_cluster));
-}
-
-void RadosTestParamPP::TearDownTestCase()
-{
-  if (cache_pool_name.length()) {
-    // tear down tiers
-    bufferlist inbl;
-    ASSERT_EQ(0, s_cluster.mon_command(
-      "{\"prefix\": \"osd tier remove-overlay\", \"pool\": \"" + pool_name +
-      "\"}",
-      inbl, NULL, NULL));
-    ASSERT_EQ(0, s_cluster.mon_command(
-      "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
-      "\", \"tierpool\": \"" + cache_pool_name + "\"}",
-      inbl, NULL, NULL));
-    ASSERT_EQ(0, s_cluster.mon_command(
-      "{\"prefix\": \"osd pool delete\", \"pool\": \"" + cache_pool_name +
-      "\", \"pool2\": \"" + cache_pool_name + "\", \"sure\": \"--yes-i-really-really-mean-it\"}",
-      inbl, NULL, NULL));
-    cache_pool_name = "";
-  }
-  ASSERT_EQ(0, destroy_one_pool_pp(pool_name, s_cluster));
-}
-
-void RadosTestParamPP::SetUp()
-{
-  if (strcmp(GetParam(), "cache") == 0 && cache_pool_name.empty()) {
-    cache_pool_name = get_temp_pool_name();
-    bufferlist inbl;
-    ASSERT_EQ(0, cluster.mon_command(
-      "{\"prefix\": \"osd pool create\", \"pool\": \"" + cache_pool_name +
-      "\", \"pg_num\": 4}",
-      inbl, NULL, NULL));
-    ASSERT_EQ(0, cluster.mon_command(
-      "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-      "\", \"tierpool\": \"" + cache_pool_name +
-      "\", \"force_nonempty\": \"--force-nonempty\" }",
-      inbl, NULL, NULL));
-    ASSERT_EQ(0, cluster.mon_command(
-      "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
-      "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
-      inbl, NULL, NULL));
-    ASSERT_EQ(0, cluster.mon_command(
-      "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
-      "\", \"mode\": \"writeback\"}",
-      inbl, NULL, NULL));
-    cluster.wait_for_latest_osdmap();
-  }
-
-  ASSERT_EQ(0, cluster.ioctx_create(pool_name.c_str(), ioctx));
-  nspace = get_temp_pool_name();
-  ioctx.set_namespace(nspace);
-  bool requires;
-  ASSERT_EQ(0, ioctx.pool_requires_alignment2(&requires));
-  ASSERT_FALSE(requires);
-}
-
-void RadosTestParamPP::TearDown()
-{
-  if (cleanup) {
-    cleanup_default_namespace(ioctx);
-    cleanup_namespace(ioctx, nspace);
-  }
-  ioctx.close();
-}
-
-void RadosTestParamPP::cleanup_default_namespace(librados::IoCtx ioctx)
-{
-  // remove all objects from the default namespace to avoid polluting
-  // other tests
-  cleanup_namespace(ioctx, "");
-}
-
-void RadosTestParamPP::cleanup_namespace(librados::IoCtx ioctx, std::string ns)
-{
-  ioctx.snap_set_read(librados::SNAP_HEAD);
-  ioctx.set_namespace(ns);
-  for (NObjectIterator it = ioctx.nobjects_begin();
-       it != ioctx.nobjects_end(); ++it) {
-    ioctx.locator_set_key(it->get_locator());
-    ASSERT_EQ(0, ioctx.remove(it->get_oid()));
-  }
-}
-
 std::string RadosTestEC::pool_name;
 rados_t RadosTestEC::s_cluster = NULL;
 
@@ -531,38 +188,3 @@ void RadosTestEC::TearDown()
   rados_ioctx_destroy(ioctx);
 }
 
-std::string RadosTestECPP::pool_name;
-Rados RadosTestECPP::s_cluster;
-
-void RadosTestECPP::SetUpTestCase()
-{
-  pool_name = get_temp_pool_name();
-  ASSERT_EQ("", create_one_ec_pool_pp(pool_name, s_cluster));
-}
-
-void RadosTestECPP::TearDownTestCase()
-{
-  ASSERT_EQ(0, destroy_one_ec_pool_pp(pool_name, s_cluster));
-}
-
-void RadosTestECPP::SetUp()
-{
-  ASSERT_EQ(0, cluster.ioctx_create(pool_name.c_str(), ioctx));
-  nspace = get_temp_pool_name();
-  ioctx.set_namespace(nspace);
-  bool requires;
-  ASSERT_EQ(0, ioctx.pool_requires_alignment2(&requires));
-  ASSERT_TRUE(requires);
-  ASSERT_EQ(0, ioctx.pool_required_alignment2(&alignment));
-  ASSERT_NE(0U, alignment);
-}
-
-void RadosTestECPP::TearDown()
-{
-  if (cleanup) {
-    cleanup_default_namespace(ioctx);
-    cleanup_namespace(ioctx, nspace);
-  }
-  ioctx.close();
-}
-
index 94f668eac01233d111ae9da075597b18ea570061..15fcfeb73bc0c7d755875a99283efc8a7e1918d7 100644 (file)
@@ -5,7 +5,6 @@
 #define CEPH_TEST_RADOS_TESTCASE_H
 
 #include "include/rados/librados.h"
-#include "include/rados/librados.hpp"
 #include "gtest/gtest.h"
 
 #include <string>
@@ -41,47 +40,6 @@ struct RadosTestNSCleanup : public RadosTestNS {
   RadosTestNSCleanup() : RadosTestNS(true) {}
 };
 
-class RadosTestPPNS : public ::testing::Test {
-public:
-  RadosTestPPNS(bool c=false) : cluster(s_cluster), cleanup(c) {}
-  ~RadosTestPPNS() override {}
-protected:
-  static void SetUpTestCase();
-  static void TearDownTestCase();
-  static void cleanup_all_objects(librados::IoCtx ioctx);
-  static librados::Rados s_cluster;
-  static std::string pool_name;
-
-  void SetUp() override;
-  void TearDown() override;
-  librados::Rados &cluster;
-  librados::IoCtx ioctx;
-  bool cleanup;
-};
-
-struct RadosTestPPNSCleanup : public RadosTestPPNS {
-  RadosTestPPNSCleanup() : RadosTestPPNS(true) {}
-};
-
-class RadosTestParamPPNS : public ::testing::TestWithParam<const char*> {
-public:
-  RadosTestParamPPNS(bool c=false) : cluster(s_cluster), cleanup(c) {}
-  ~RadosTestParamPPNS() override {}
-  static void SetUpTestCase();
-  static void TearDownTestCase();
-protected:
-  static void cleanup_all_objects(librados::IoCtx ioctx);
-  static librados::Rados s_cluster;
-  static std::string pool_name;
-  static std::string cache_pool_name;
-
-  void SetUp() override;
-  void TearDown() override;
-  librados::Rados &cluster;
-  librados::IoCtx ioctx;
-  bool cleanup;
-};
-
 class RadosTestECNS : public RadosTestNS {
 public:
   RadosTestECNS(bool c=false) : cleanup(c) {}
@@ -104,28 +62,6 @@ struct RadosTestECNSCleanup : public RadosTestECNS {
   RadosTestECNSCleanup() : RadosTestECNS(true) {}
 };
 
-class RadosTestECPPNS : public RadosTestPPNS {
-public:
-  RadosTestECPPNS(bool c=false) : cluster(s_cluster), cleanup(c) {}
-  ~RadosTestECPPNS() override {}
-protected:
-  static void SetUpTestCase();
-  static void TearDownTestCase();
-  static librados::Rados s_cluster;
-  static std::string pool_name;
-
-  void SetUp() override;
-  void TearDown() override;
-  librados::Rados &cluster;
-  librados::IoCtx ioctx;
-  uint64_t alignment = 0;
-  bool cleanup;
-};
-
-struct RadosTestECPPNSCleanup : public RadosTestECPPNS {
-  RadosTestECPPNSCleanup() : RadosTestECPPNS(true) {}
-};
-
 /**
  * These test cases create a temporary pool that lives as long as the
  * test case.  Each test within a test case gets a new ioctx set to a
@@ -154,47 +90,6 @@ protected:
   bool cleanup;
 };
 
-class RadosTestPP : public ::testing::Test {
-public:
-  RadosTestPP(bool c=false) : cluster(s_cluster), cleanup(c) {}
-  ~RadosTestPP() override {}
-protected:
-  static void SetUpTestCase();
-  static void TearDownTestCase();
-  static void cleanup_default_namespace(librados::IoCtx ioctx);
-  static void cleanup_namespace(librados::IoCtx ioctx, std::string ns);
-  static librados::Rados s_cluster;
-  static std::string pool_name;
-
-  void SetUp() override;
-  void TearDown() override;
-  librados::Rados &cluster;
-  librados::IoCtx ioctx;
-  bool cleanup;
-  std::string nspace;
-};
-
-class RadosTestParamPP : public ::testing::TestWithParam<const char*> {
-public:
-  RadosTestParamPP(bool c=false) : cluster(s_cluster), cleanup(c) {}
-  ~RadosTestParamPP() override {}
-  static void SetUpTestCase();
-  static void TearDownTestCase();
-protected:
-  static void cleanup_default_namespace(librados::IoCtx ioctx);
-  static void cleanup_namespace(librados::IoCtx ioctx, std::string ns);
-  static librados::Rados s_cluster;
-  static std::string pool_name;
-  static std::string cache_pool_name;
-
-  void SetUp() override;
-  void TearDown() override;
-  librados::Rados &cluster;
-  librados::IoCtx ioctx;
-  bool cleanup;
-  std::string nspace;
-};
-
 class RadosTestEC : public RadosTest {
 public:
   RadosTestEC(bool c=false) : cleanup(c) {}
@@ -214,25 +109,6 @@ protected:
   uint64_t alignment = 0;
 };
 
-class RadosTestECPP : public RadosTestPP {
-public:
-  RadosTestECPP(bool c=false) : cluster(s_cluster), cleanup(c) {}
-  ~RadosTestECPP() override {}
-protected:
-  static void SetUpTestCase();
-  static void TearDownTestCase();
-  static librados::Rados s_cluster;
-  static std::string pool_name;
-
-  void SetUp() override;
-  void TearDown() override;
-  librados::Rados &cluster;
-  librados::IoCtx ioctx;
-  bool cleanup;
-  std::string nspace;
-  uint64_t alignment = 0;
-};
-
 /**
  * Test case without creating a temporary pool in advance.
  * This is necessary for scenarios such that we need to
index 2d14e0f9170019ca8240c870c25ea780aa9b6496..503f992c06912ef6ff481e5b13b519d711015f5d 100644 (file)
@@ -1,22 +1,23 @@
-#include "common/errno.h"
+#include <errno.h>
+#include <fcntl.h>
+#include <string>
+#include <sstream>
+#include <utility>
+#include <boost/scoped_ptr.hpp>
+
 #include "include/err.h"
 #include "include/rados/librados.h"
-#include "test/librados/test.h"
 #include "include/types.h"
 #include "include/stringify.h"
 #include "include/scope_guard.h"
 
+#include "common/errno.h"
+
 #include "gtest/gtest.h"
-#include <errno.h>
-#include <fcntl.h>
-#include <sstream>
-#include <string>
-#include <boost/scoped_ptr.hpp>
-#include <utility>
+
+#include "test.h"
 
 using std::ostringstream;
-using namespace librados;
-using std::pair;
 
 class AioTestData
 {
@@ -63,55 +64,6 @@ public:
   bool m_init;
 };
 
-class AioTestDataPP
-{
-public:
-  AioTestDataPP()
-    : m_init(false)
-  {
-  }
-
-  ~AioTestDataPP()
-  {
-    if (m_init) {
-      m_ioctx.close();
-      destroy_one_pool_pp(m_pool_name, m_cluster);
-    }
-  }
-
-  std::string init()
-  {
-      return init({});
-  }
-
-  std::string init(const std::map<std::string, std::string> &config)
-  {
-    int ret;
-
-    m_pool_name = get_temp_pool_name();
-    std::string err = create_one_pool_pp(m_pool_name, m_cluster, config);
-    if (!err.empty()) {
-      ostringstream oss;
-      oss << "create_one_pool(" << m_pool_name << ") failed: error " << err;
-      return oss.str();
-    }
-    ret = m_cluster.ioctx_create(m_pool_name.c_str(), m_ioctx);
-    if (ret) {
-      destroy_one_pool_pp(m_pool_name, m_cluster);
-      ostringstream oss;
-      oss << "rados_ioctx_create failed: error " << ret;
-      return oss.str();
-    }
-    m_init = true;
-    return "";
-  }
-
-  Rados m_cluster;
-  IoCtx m_ioctx;
-  std::string m_pool_name;
-  bool m_init;
-};
-
 TEST(LibRadosAio, TooBig) {
   AioTestData test_data;
   rados_completion_t my_completion;
@@ -129,75 +81,6 @@ TEST(LibRadosAio, TooBig) {
   rados_aio_release(my_completion);
 }
 
-TEST(LibRadosAio, TooBigPP) {
-  AioTestDataPP test_data;
-  ASSERT_EQ("", test_data.init());
-
-  bufferlist bl;
-  AioCompletion *aio_completion = test_data.m_cluster.aio_create_completion(
-                                                                            nullptr, NULL, NULL);
-  ASSERT_EQ(-E2BIG, test_data.m_ioctx.aio_write("foo", aio_completion, bl, UINT_MAX, 0));
-  ASSERT_EQ(-E2BIG, test_data.m_ioctx.aio_append("foo", aio_completion, bl, UINT_MAX));
-  // ioctx.aio_write_full no way to overflow bl.length()
-  delete aio_completion;
-}
-
-TEST(LibRadosAio, PoolQuotaPP) {
-  AioTestDataPP test_data;
-  ASSERT_EQ("", test_data.init());
-  string p = get_temp_pool_name();
-  ASSERT_EQ(0, test_data.m_cluster.pool_create(p.c_str()));
-  IoCtx ioctx;
-  ASSERT_EQ(0, test_data.m_cluster.ioctx_create(p.c_str(), ioctx));
-  ioctx.application_enable("rados", true);
-
-  bufferlist inbl;
-  ASSERT_EQ(0, test_data.m_cluster.mon_command(
-      "{\"prefix\": \"osd pool set-quota\", \"pool\": \"" + p +
-      "\", \"field\": \"max_bytes\", \"val\": \"4096\"}",
-      inbl, NULL, NULL));
-
-  bufferlist bl;
-  bufferptr z(4096);
-  bl.append(z);
-  int n;
-  for (n = 0; n < 1024; ++n) {
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    librados::AioCompletion *completion =
-      test_data.m_cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate(
-       "foo" + stringify(n), completion, &op,
-       librados::OPERATION_FULL_TRY));
-    completion->wait_for_safe();
-    int r = completion->get_return_value();
-    completion->release();
-    if (r == -EDQUOT)
-      break;
-    ASSERT_EQ(0, r);
-    sleep(1);
-  }
-  ASSERT_LT(n, 1024);
-
-  // make sure we have latest map that marked the pool full
-  test_data.m_cluster.wait_for_latest_osdmap();
-
-  // make sure we block without FULL_TRY
-  {
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    librados::AioCompletion *completion =
-      test_data.m_cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate("bar", completion, &op, 0));
-    sleep(5);
-    ASSERT_FALSE(completion->is_safe());
-    completion->release();
-  }
-
-  ioctx.close();
-  ASSERT_EQ(0, test_data.m_cluster.pool_delete(p.c_str()));
-}
-
 TEST(LibRadosAio, SimpleWrite) {
   AioTestData test_data;
   rados_completion_t my_completion;
@@ -229,45 +112,6 @@ TEST(LibRadosAio, SimpleWrite) {
   ASSERT_EQ(0, rados_aio_get_return_value(my_completion2));
 }
 
-TEST(LibRadosAio, SimpleWritePP) {
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  {
-  AioTestDataPP test_data;
-  ASSERT_EQ("", test_data.init());
-  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  AioCompletion *my_completion_null = NULL;
-  ASSERT_NE(my_completion, my_completion_null);
-  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo",
-                              my_completion, bl1, sizeof(buf), 0));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion->get_return_value());
-  delete my_completion;
-  }
-
-  {
-  AioTestDataPP test_data;
-  ASSERT_EQ("", test_data.init());
-  test_data.m_ioctx.set_namespace("nspace");
-  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo",
-                              my_completion, bl1, sizeof(buf), 0));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion->get_return_value());
-  delete my_completion;
-  }
-}
-
 TEST(LibRadosAio, WaitForSafe) {
   AioTestData test_data;
   rados_completion_t my_completion;
@@ -284,25 +128,6 @@ TEST(LibRadosAio, WaitForSafe) {
   rados_aio_release(my_completion);
 }
 
-TEST(LibRadosAio, WaitForSafePP) {
-  AioTestDataPP test_data;
-  ASSERT_EQ("", test_data.init());
-  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  AioCompletion *my_completion_null = NULL;
-  ASSERT_NE(my_completion, my_completion_null);
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo",
-                              my_completion, bl1, sizeof(buf), 0));
-  TestAlarm alarm;
-  ASSERT_EQ(0, my_completion->wait_for_safe());
-  ASSERT_EQ(0, my_completion->get_return_value());
-  delete my_completion;
-}
-
 TEST(LibRadosAio, RoundTrip) {
   AioTestData test_data;
   rados_completion_t my_completion;
@@ -426,167 +251,6 @@ TEST(LibRadosAio, RoundTrip3) {
   ASSERT_EQ(bl.crc32c(-1), checksum[1]);
 }
 
-TEST(LibRadosAio, RoundTripPP) {
-  AioTestDataPP test_data;
-  ASSERT_EQ("", test_data.init());
-  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  AioCompletion *my_completion_null = NULL;
-  ASSERT_NE(my_completion, my_completion_null);
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
-                                          bl1, sizeof(buf), 0));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion->get_return_value());
-  bufferlist bl2;
-  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  ASSERT_NE(my_completion2, my_completion_null);
-  ASSERT_EQ(0, test_data.m_ioctx.aio_read("foo",
-                             my_completion2, &bl2, sizeof(buf), 0));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion2->wait_for_complete());
-  }
-  ASSERT_EQ((int)sizeof(buf), my_completion2->get_return_value());
-  ASSERT_EQ(sizeof(buf), bl2.length());
-  ASSERT_EQ(0, memcmp(buf, bl2.c_str(), sizeof(buf)));
-  delete my_completion;
-  delete my_completion2;
-}
-
-TEST(LibRadosAio, RoundTripPP2) {
-  AioTestDataPP test_data;
-  ASSERT_EQ("", test_data.init());
-  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  AioCompletion *my_completion_null = NULL;
-  ASSERT_NE(my_completion, my_completion_null);
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
-                                          bl1, sizeof(buf), 0));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion->get_return_value());
-  bufferlist bl2;
-  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  ASSERT_NE(my_completion2, my_completion_null);
-  ASSERT_EQ(0, test_data.m_ioctx.aio_read("foo",
-                             my_completion2, &bl2, sizeof(buf), 0));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion2->wait_for_safe());
-    ASSERT_EQ(0, my_completion2->wait_for_complete());
-  }
-  ASSERT_EQ((int)sizeof(buf), my_completion2->get_return_value());
-  ASSERT_EQ(sizeof(buf), bl2.length());
-  ASSERT_EQ(0, memcmp(buf, bl2.c_str(), sizeof(buf)));
-  delete my_completion;
-  delete my_completion2;
-}
-
-//using ObjectWriteOperation/ObjectReadOperation with iohint
-TEST(LibRadosAio, RoundTripPP3)
-{
-  Rados cluster;
-  std::string pool_name = get_temp_pool_name();
-  ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
-  IoCtx ioctx;
-  cluster.ioctx_create(pool_name.c_str(), ioctx);
-
-  boost::scoped_ptr<AioCompletion> my_completion1(cluster.aio_create_completion(0, 0, 0));
-  ObjectWriteOperation op;
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl;
-  bl.append(buf, sizeof(buf));
-
-  op.write(0, bl);
-  op.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
-  ioctx.aio_operate("test_obj", my_completion1.get(), &op);
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion1->wait_for_complete());
-  }
-  EXPECT_EQ(0, my_completion1->get_return_value());
-
-  boost::scoped_ptr<AioCompletion> my_completion2(cluster.aio_create_completion(0, 0, 0));
-  bl.clear();
-  ObjectReadOperation op1;
-  op1.read(0, sizeof(buf), &bl, NULL);
-  op1.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED|LIBRADOS_OP_FLAG_FADVISE_RANDOM);
-  bufferlist init_value_bl;
-  encode(static_cast<int32_t>(-1), init_value_bl);
-  bufferlist csum_bl;
-  op1.checksum(LIBRADOS_CHECKSUM_TYPE_CRC32C, init_value_bl,
-              0, 0, 0, &csum_bl, nullptr);
-  ioctx.aio_operate("test_obj", my_completion2.get(), &op1, 0);
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion2->wait_for_complete());
-  }
-  EXPECT_EQ(0, my_completion2->get_return_value());
-  ASSERT_EQ(0, memcmp(buf, bl.c_str(), sizeof(buf)));
-
-  ASSERT_EQ(8U, csum_bl.length());
-  auto csum_bl_it = csum_bl.cbegin();
-  uint32_t csum_count;
-  uint32_t csum;
-  decode(csum_count, csum_bl_it);
-  ASSERT_EQ(1U, csum_count);
-  decode(csum, csum_bl_it);
-  ASSERT_EQ(bl.crc32c(-1), csum);
-  ioctx.remove("test_obj");
-  destroy_one_pool_pp(pool_name, cluster);
-}
-
-TEST(LibRadosAio, RoundTripSparseReadPP) {
-  AioTestDataPP test_data;
-  ASSERT_EQ("", test_data.init());
-  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  AioCompletion *my_completion_null = NULL;
-  ASSERT_NE(my_completion, my_completion_null);
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
-                                          bl1, sizeof(buf), 0));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion->get_return_value());
-  std::map<uint64_t, uint64_t> extents;
-  bufferlist bl2;
-  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  ASSERT_NE(my_completion2, my_completion_null);
-  ASSERT_EQ(0, test_data.m_ioctx.aio_sparse_read("foo",
-                             my_completion2, &extents, &bl2, sizeof(buf), 0));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion2->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion2->get_return_value());
-  assert_eq_sparse(bl1, extents, bl2);
-  delete my_completion;
-  delete my_completion2;
-}
-
 TEST(LibRadosAio, RoundTripAppend) {
   AioTestData test_data;
   rados_completion_t my_completion, my_completion2, my_completion3;
@@ -631,57 +295,6 @@ TEST(LibRadosAio, RoundTripAppend) {
   rados_aio_release(my_completion3);
 }
 
-TEST(LibRadosAio, RoundTripAppendPP) {
-  AioTestDataPP test_data;
-  ASSERT_EQ("", test_data.init());
-  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  AioCompletion *my_completion_null = NULL;
-  ASSERT_NE(my_completion, my_completion_null);
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, test_data.m_ioctx.aio_append("foo", my_completion,
-                                           bl1, sizeof(buf)));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion->get_return_value());
-  char buf2[128];
-  memset(buf2, 0xdd, sizeof(buf2));
-  bufferlist bl2;
-  bl2.append(buf2, sizeof(buf2));
-  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  ASSERT_NE(my_completion2, my_completion_null);
-  ASSERT_EQ(0, test_data.m_ioctx.aio_append("foo", my_completion2,
-                                           bl2, sizeof(buf2)));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion2->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion2->get_return_value());
-  bufferlist bl3;
-  AioCompletion *my_completion3 = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  ASSERT_NE(my_completion3, my_completion_null);
-  ASSERT_EQ(0, test_data.m_ioctx.aio_read("foo",
-                             my_completion3, &bl3, 2 * sizeof(buf), 0));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion3->wait_for_complete());
-  }
-  ASSERT_EQ((int)(sizeof(buf) * 2), my_completion3->get_return_value());
-  ASSERT_EQ(sizeof(buf) * 2, bl3.length());
-  ASSERT_EQ(0, memcmp(bl3.c_str(), buf, sizeof(buf)));
-  ASSERT_EQ(0, memcmp(bl3.c_str() + sizeof(buf), buf2, sizeof(buf2)));
-  delete my_completion;
-  delete my_completion2;
-  delete my_completion3;
-}
-
 TEST(LibRadosAio, RemoveTest) {
   char buf[128];
   char buf2[sizeof(buf)];
@@ -703,27 +316,6 @@ TEST(LibRadosAio, RemoveTest) {
   rados_aio_release(my_completion);
 }
 
-TEST(LibRadosAioPP, RemoveTestPP) {
-  char buf[128];
-  memset(buf, 0xaa, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  AioTestDataPP test_data;
-  ASSERT_EQ("", test_data.init());
-  ASSERT_EQ(0, test_data.m_ioctx.append("foo", bl1, sizeof(buf)));
-  boost::scoped_ptr<AioCompletion> my_completion
-    (test_data.m_cluster.aio_create_completion
-     (nullptr, nullptr, nullptr));
-  ASSERT_EQ(0, test_data.m_ioctx.aio_remove("foo", my_completion.get()));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion->get_return_value());
-  bufferlist bl2;
-  ASSERT_EQ(-ENOENT, test_data.m_ioctx.read("foo", bl2, sizeof(buf), 0));
-}
-
 TEST(LibRadosAio, XattrsRoundTrip) {
   char buf[128];
   char attr1[] = "attr1";
@@ -770,59 +362,6 @@ TEST(LibRadosAio, XattrsRoundTrip) {
   ASSERT_EQ(0, memcmp(attr1_buf, buf, sizeof(attr1_buf)));
 }
 
-TEST(LibRadosAioPP, XattrsRoundTripPP) {
-  char buf[128];
-  char attr1[] = "attr1";
-  char attr1_buf[] = "foo bar baz";
-  memset(buf, 0xaa, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  AioTestDataPP test_data;
-  ASSERT_EQ("", test_data.init());
-  ASSERT_EQ(0, test_data.m_ioctx.append("foo", bl1, sizeof(buf)));
-  bufferlist bl2;
-  // async getxattr
-  boost::scoped_ptr<AioCompletion> my_completion
-    (test_data.m_cluster.aio_create_completion
-     (nullptr, nullptr, nullptr));
-  ASSERT_EQ(0, test_data.m_ioctx.aio_getxattr("foo", my_completion.get(), attr1, bl2));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion->wait_for_complete());
-  }
-  ASSERT_EQ(-ENODATA, my_completion->get_return_value());
-  // append
-  bufferlist bl3;
-  bl3.append(attr1_buf, sizeof(attr1_buf));
-  // async setxattr
-  AioTestDataPP test_data2;
-  ASSERT_EQ("", test_data2.init());
-  boost::scoped_ptr<AioCompletion> my_completion2
-    (test_data.m_cluster.aio_create_completion
-     (nullptr, nullptr, nullptr));
-  ASSERT_EQ(0, test_data.m_ioctx.aio_setxattr("foo", my_completion2.get(), attr1, bl3));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion2->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion2->get_return_value());
-  // async getxattr
-  bufferlist bl4;
-  AioTestDataPP test_data3;
-  ASSERT_EQ("", test_data3.init());
-  boost::scoped_ptr<AioCompletion> my_completion3
-    (test_data.m_cluster.aio_create_completion
-     (nullptr, nullptr, nullptr));
-  ASSERT_EQ(0, test_data.m_ioctx.aio_getxattr("foo", my_completion3.get(), attr1, bl4));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion3->wait_for_complete());
-  }
-  ASSERT_EQ((int)sizeof(attr1_buf), my_completion3->get_return_value());
-  // check content of attribute
-  ASSERT_EQ(0, memcmp(bl4.c_str(), attr1_buf, sizeof(attr1_buf)));
-}
-
 TEST(LibRadosAio, RmXattr) {
   char buf[128];
   char attr1[] = "attr1";
@@ -897,91 +436,6 @@ TEST(LibRadosAio, RmXattr) {
   rados_aio_release(my_completion5);
 }
 
-TEST(LibRadosAioPP, RmXattrPP) {
-  char buf[128];
-  char attr1[] = "attr1";
-  char attr1_buf[] = "foo bar baz";
-  memset(buf, 0xaa, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  AioTestDataPP test_data;
-  ASSERT_EQ("", test_data.init());
-  ASSERT_EQ(0, test_data.m_ioctx.append("foo", bl1, sizeof(buf)));
-  // async setxattr
-  bufferlist bl2;
-  bl2.append(attr1_buf, sizeof(attr1_buf));
-  boost::scoped_ptr<AioCompletion> my_completion
-    (test_data.m_cluster.aio_create_completion
-     (nullptr, nullptr, nullptr));
-  ASSERT_EQ(0, test_data.m_ioctx.aio_setxattr("foo", my_completion.get(), attr1, bl2));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion->get_return_value());
-  // async rmxattr
-  AioTestDataPP test_data2;
-  ASSERT_EQ("", test_data2.init());
-  boost::scoped_ptr<AioCompletion> my_completion2
-    (test_data.m_cluster.aio_create_completion
-     (nullptr, nullptr, nullptr));
-  ASSERT_EQ(0, test_data.m_ioctx.aio_rmxattr("foo", my_completion2.get(), attr1));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion2->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion2->get_return_value());
-  // async getxattr
-  AioTestDataPP test_data3;
-  ASSERT_EQ("", test_data3.init());
-  boost::scoped_ptr<AioCompletion> my_completion3
-    (test_data.m_cluster.aio_create_completion
-     (nullptr, nullptr, nullptr));
-  bufferlist bl3;
-  ASSERT_EQ(0, test_data.m_ioctx.aio_getxattr("foo", my_completion3.get(), attr1, bl3));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion3->wait_for_complete());
-  }
-  ASSERT_EQ(-ENODATA, my_completion3->get_return_value());
-  // Test rmxattr on a removed object
-  char buf2[128];
-  char attr2[] = "attr2";
-  char attr2_buf[] = "foo bar baz";
-  memset(buf2, 0xbb, sizeof(buf2));
-  bufferlist bl21;
-  bl21.append(buf, sizeof(buf));
-  ASSERT_EQ(0, test_data.m_ioctx.write("foo_rmxattr", bl21, sizeof(buf2), 0));
-  bufferlist bl22;
-  bl22.append(attr2_buf, sizeof(attr2_buf));
-  // async setxattr
-  AioTestDataPP test_data4;
-  ASSERT_EQ("", test_data4.init());
-  boost::scoped_ptr<AioCompletion> my_completion4
-    (test_data.m_cluster.aio_create_completion
-     (nullptr, nullptr, nullptr));
-  ASSERT_EQ(0, test_data.m_ioctx.aio_setxattr("foo_rmxattr", my_completion4.get(), attr2, bl22));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion4->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion4->get_return_value());
-  // remove object
-  ASSERT_EQ(0, test_data.m_ioctx.remove("foo_rmxattr"));
-  // async rmxattr on non existing object
-  AioTestDataPP test_data5;
-  ASSERT_EQ("", test_data5.init());
-  boost::scoped_ptr<AioCompletion> my_completion5
-    (test_data.m_cluster.aio_create_completion
-     (nullptr, nullptr, nullptr));
-  ASSERT_EQ(0, test_data.m_ioctx.aio_rmxattr("foo_rmxattr", my_completion5.get(), attr2));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion5->wait_for_complete());
-  }
-  ASSERT_EQ(-ENOENT, my_completion5->get_return_value());
-}
-
 TEST(LibRadosAio, XattrIter) {
   AioTestData test_data;
   ASSERT_EQ("", test_data.init());
@@ -1000,7 +454,7 @@ TEST(LibRadosAio, XattrIter) {
   ASSERT_EQ(0, rados_setxattr(test_data.m_ioctx, "foo", attr2, attr2_buf, sizeof(attr2_buf)));
   // call async version of getxattrs and wait for completion
   rados_completion_t my_completion;
-  ASSERT_EQ(0, rados_aio_create_completion(nullptr,
+  ASSERT_EQ(0, rados_aio_create_completion((void*)&test_data,
             nullptr, nullptr, &my_completion));
   rados_xattrs_iter_t iter;
   ASSERT_EQ(0, rados_aio_getxattrs(test_data.m_ioctx, "foo", my_completion, &iter));
@@ -1035,53 +489,6 @@ TEST(LibRadosAio, XattrIter) {
   rados_getxattrs_end(iter);
 }
 
-TEST(LibRadosIoPP, XattrListPP) {
-  AioTestDataPP test_data;
-  ASSERT_EQ("", test_data.init());
-  // create an object with 2 attributes
-  char buf[128];
-  char attr1[] = "attr1";
-  char attr1_buf[] = "foo bar baz";
-  char attr2[] = "attr2";
-  char attr2_buf[256];
-  for (size_t j = 0; j < sizeof(attr2_buf); ++j) {
-    attr2_buf[j] = j % 0xff;
-  }
-  memset(buf, 0xaa, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, test_data.m_ioctx.append("foo", bl1, sizeof(buf)));
-  bufferlist bl2;
-  bl2.append(attr1_buf, sizeof(attr1_buf));
-  ASSERT_EQ(0, test_data.m_ioctx.setxattr("foo", attr1, bl2));
-  bufferlist bl3;
-  bl3.append(attr2_buf, sizeof(attr2_buf));
-  ASSERT_EQ(0, test_data.m_ioctx.setxattr("foo", attr2, bl3));
-  // call async version of getxattrs
-  boost::scoped_ptr<AioCompletion> my_completion
-    (test_data.m_cluster.aio_create_completion
-     (nullptr, nullptr, nullptr));
-  std::map<std::string, bufferlist> attrset;
-  ASSERT_EQ(0, test_data.m_ioctx.aio_getxattrs("foo", my_completion.get(), attrset));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion->get_return_value());
-  for (std::map<std::string, bufferlist>::iterator i = attrset.begin();
-       i != attrset.end(); ++i) {
-    if (i->first == string(attr1)) {
-      ASSERT_EQ(0, memcmp(i->second.c_str(), attr1_buf, sizeof(attr1_buf)));
-    }
-    else if (i->first == string(attr2)) {
-      ASSERT_EQ(0, memcmp(i->second.c_str(), attr2_buf, sizeof(attr2_buf)));
-    }
-    else {
-      ASSERT_EQ(0, 1);
-    }
-  }
-}
-
 TEST(LibRadosAio, IsComplete) {
   AioTestData test_data;
   rados_completion_t my_completion;
@@ -1121,48 +528,6 @@ TEST(LibRadosAio, IsComplete) {
   rados_aio_release(my_completion2);
 }
 
-TEST(LibRadosAio, IsCompletePP) {
-  AioTestDataPP test_data;
-  ASSERT_EQ("", test_data.init());
-  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  AioCompletion *my_completion_null = NULL;
-  ASSERT_NE(my_completion, my_completion_null);
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
-                                          bl1, sizeof(buf), 0));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion->get_return_value());
-  bufferlist bl2;
-  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  ASSERT_NE(my_completion2, my_completion_null);
-  ASSERT_EQ(0, test_data.m_ioctx.aio_read("foo", my_completion2,
-                                         &bl2, sizeof(buf), 0));
-  {
-    TestAlarm alarm;
-
-    // Busy-wait until the AIO completes.
-    // Normally we wouldn't do this, but we want to test is_complete.
-    while (true) {
-      int is_complete = my_completion2->is_complete();
-      if (is_complete)
-       break;
-    }
-  }
-  ASSERT_EQ((int)sizeof(buf), my_completion2->get_return_value());
-  ASSERT_EQ(sizeof(buf), bl2.length());
-  ASSERT_EQ(0, memcmp(buf, bl2.c_str(), sizeof(buf)));
-  delete my_completion;
-  delete my_completion2;
-}
-
 TEST(LibRadosAio, IsSafe) {
   AioTestData test_data;
   rados_completion_t my_completion;
@@ -1202,54 +567,12 @@ TEST(LibRadosAio, IsSafe) {
   rados_aio_release(my_completion2);
 }
 
-TEST(LibRadosAio, IsSafePP) {
-  AioTestDataPP test_data;
+TEST(LibRadosAio, ReturnValue) {
+  AioTestData test_data;
+  rados_completion_t my_completion;
   ASSERT_EQ("", test_data.init());
-  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  AioCompletion *my_completion_null = NULL;
-  ASSERT_NE(my_completion, my_completion_null);
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
-                                          bl1, sizeof(buf), 0));
-  {
-    TestAlarm alarm;
-
-    // Busy-wait until the AIO completes.
-    // Normally we wouldn't do this, but we want to test rados_aio_is_safe.
-    while (true) {
-      int is_safe = my_completion->is_safe();
-      if (is_safe)
-       break;
-    }
-  }
-  ASSERT_EQ(0, my_completion->get_return_value());
-  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  bufferlist bl2;
-  ASSERT_NE(my_completion2, my_completion_null);
-  ASSERT_EQ(0, test_data.m_ioctx.aio_read("foo", my_completion2,
-                                         &bl2, sizeof(buf), 0));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion2->wait_for_complete());
-  }
-  ASSERT_EQ((int)sizeof(buf), my_completion2->get_return_value());
-  ASSERT_EQ(sizeof(buf), bl2.length());
-  ASSERT_EQ(0, memcmp(buf, bl2.c_str(), sizeof(buf)));
-  delete my_completion;
-  delete my_completion2;
-}
-
-TEST(LibRadosAio, ReturnValue) {
-  AioTestData test_data;
-  rados_completion_t my_completion;
-  ASSERT_EQ("", test_data.init());
-  ASSERT_EQ(0, rados_aio_create_completion(nullptr,
-             nullptr, nullptr, &my_completion));
+  ASSERT_EQ(0, rados_aio_create_completion(nullptr,
+             nullptr, nullptr, &my_completion));
   char buf[128];
   memset(buf, 0, sizeof(buf));
   ASSERT_EQ(0, rados_aio_read(test_data.m_ioctx, "nonexistent",
@@ -1262,24 +585,6 @@ TEST(LibRadosAio, ReturnValue) {
   rados_aio_release(my_completion);
 }
 
-TEST(LibRadosAio, ReturnValuePP) {
-  AioTestDataPP test_data;
-  ASSERT_EQ("", test_data.init());
-  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  AioCompletion *my_completion_null = NULL;
-  ASSERT_NE(my_completion, my_completion_null);
-  bufferlist bl1;
-  ASSERT_EQ(0, test_data.m_ioctx.aio_read("nonexistent",
-                              my_completion, &bl1, 128, 0));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion->wait_for_complete());
-  }
-  ASSERT_EQ(-ENOENT, my_completion->get_return_value());
-  delete my_completion;
-}
-
 TEST(LibRadosAio, Flush) {
   AioTestData test_data;
   rados_completion_t my_completion;
@@ -1309,38 +614,6 @@ TEST(LibRadosAio, Flush) {
   rados_aio_release(my_completion2);
 }
 
-TEST(LibRadosAio, FlushPP) {
-  AioTestDataPP test_data;
-  ASSERT_EQ("", test_data.init());
-  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  AioCompletion *my_completion_null = NULL;
-  ASSERT_NE(my_completion, my_completion_null);
-  char buf[128];
-  memset(buf, 0xee, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
-                                          bl1, sizeof(buf), 0));
-  ASSERT_EQ(0, test_data.m_ioctx.aio_flush());
-  ASSERT_EQ(0, my_completion->get_return_value());
-  bufferlist bl2;
-  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  ASSERT_NE(my_completion2, my_completion_null);
-  ASSERT_EQ(0, test_data.m_ioctx.aio_read("foo", my_completion2,
-                                         &bl2, sizeof(buf), 0));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion2->wait_for_complete());
-  }
-  ASSERT_EQ((int)sizeof(buf), my_completion2->get_return_value());
-  ASSERT_EQ(sizeof(buf), bl2.length());
-  ASSERT_EQ(0, memcmp(buf, bl2.c_str(), sizeof(buf)));
-  delete my_completion;
-  delete my_completion2;
-}
-
 TEST(LibRadosAio, FlushAsync) {
   AioTestData test_data;
   rados_completion_t my_completion;
@@ -1382,50 +655,6 @@ TEST(LibRadosAio, FlushAsync) {
   rados_aio_release(flush_completion);
 }
 
-TEST(LibRadosAio, FlushAsyncPP) {
-  AioTestDataPP test_data;
-  ASSERT_EQ("", test_data.init());
-  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  AioCompletion *flush_completion =
-      test_data.m_cluster.aio_create_completion(NULL, NULL, NULL);
-  AioCompletion *my_completion_null = NULL;
-  ASSERT_NE(my_completion, my_completion_null);
-  char buf[128];
-  memset(buf, 0xee, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
-                                          bl1, sizeof(buf), 0));
-  ASSERT_EQ(0, test_data.m_ioctx.aio_flush_async(flush_completion));
-  {
-      TestAlarm alarm;
-      ASSERT_EQ(0, flush_completion->wait_for_complete());
-      ASSERT_EQ(0, flush_completion->wait_for_safe());
-  }
-  ASSERT_EQ(1, my_completion->is_complete());
-  ASSERT_EQ(1, my_completion->is_safe());
-  ASSERT_EQ(1, flush_completion->is_complete());
-  ASSERT_EQ(1, flush_completion->is_safe());
-  ASSERT_EQ(0, my_completion->get_return_value());
-  bufferlist bl2;
-  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  ASSERT_NE(my_completion2, my_completion_null);
-  ASSERT_EQ(0, test_data.m_ioctx.aio_read("foo", my_completion2,
-                                         &bl2, sizeof(buf), 0));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion2->wait_for_complete());
-  }
-  ASSERT_EQ((int)sizeof(buf), my_completion2->get_return_value());
-  ASSERT_EQ(sizeof(buf), bl2.length());
-  ASSERT_EQ(0, memcmp(buf, bl2.c_str(), sizeof(buf)));
-  delete my_completion;
-  delete my_completion2;
-  delete flush_completion;
-}
-
 TEST(LibRadosAio, RoundTripWriteFull) {
   AioTestData test_data;
   rados_completion_t my_completion, my_completion2, my_completion3;
@@ -1469,97 +698,6 @@ TEST(LibRadosAio, RoundTripWriteFull) {
   rados_aio_release(my_completion3);
 }
 
-TEST(LibRadosAio, RoundTripWriteFullPP) {
-  AioTestDataPP test_data;
-  ASSERT_EQ("", test_data.init());
-  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  AioCompletion *my_completion_null = NULL;
-  ASSERT_NE(my_completion, my_completion_null);
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
-                                          bl1, sizeof(buf), 0));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion->get_return_value());
-  char buf2[64];
-  memset(buf2, 0xdd, sizeof(buf2));
-  bufferlist bl2;
-  bl2.append(buf2, sizeof(buf2));
-  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  ASSERT_NE(my_completion2, my_completion_null);
-  ASSERT_EQ(0, test_data.m_ioctx.aio_write_full("foo", my_completion2, bl2));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion2->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion2->get_return_value());
-  bufferlist bl3;
-  AioCompletion *my_completion3 = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  ASSERT_NE(my_completion3, my_completion_null);
-  ASSERT_EQ(0, test_data.m_ioctx.aio_read("foo", my_completion3,
-                                         &bl3, sizeof(buf), 0));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion3->wait_for_complete());
-  }
-  ASSERT_EQ((int)sizeof(buf2), my_completion3->get_return_value());
-  ASSERT_EQ(sizeof(buf2), bl3.length());
-  ASSERT_EQ(0, memcmp(bl3.c_str(), buf2, sizeof(buf2)));
-  delete my_completion;
-  delete my_completion2;
-  delete my_completion3;
-}
-
-//using ObjectWriteOperation/ObjectReadOperation with iohint
-TEST(LibRadosAio, RoundTripWriteFullPP2)
-{
-  Rados cluster;
-  std::string pool_name = get_temp_pool_name();
-  ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
-  IoCtx ioctx;
-  cluster.ioctx_create(pool_name.c_str(), ioctx);
-
-  boost::scoped_ptr<AioCompletion> my_completion1(cluster.aio_create_completion(0, 0, 0));
-  ObjectWriteOperation op;
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl;
-  bl.append(buf);
-
-  op.write_full(bl);
-  op.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
-  ioctx.aio_operate("test_obj", my_completion1.get(), &op);
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion1->wait_for_complete());
-  }
-  EXPECT_EQ(0, my_completion1->get_return_value());
-
-  boost::scoped_ptr<AioCompletion> my_completion2(cluster.aio_create_completion(0, 0, 0));
-  bl.clear();
-  ObjectReadOperation op1;
-  op1.read(0, sizeof(buf), &bl, NULL);
-  op1.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED|LIBRADOS_OP_FLAG_FADVISE_RANDOM);
-  ioctx.aio_operate("test_obj", my_completion2.get(), &op1, 0);
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion2->wait_for_complete());
-  }
-  EXPECT_EQ(0, my_completion2->get_return_value());
-  ASSERT_EQ(0, memcmp(buf, bl.c_str(), sizeof(buf)));
-
-  ioctx.remove("test_obj");
-  destroy_one_pool_pp(pool_name, cluster);
-}
-
 TEST(LibRadosAio, RoundTripWriteSame) {
   AioTestData test_data;
   rados_completion_t my_completion, my_completion2, my_completion3;
@@ -1606,110 +744,6 @@ TEST(LibRadosAio, RoundTripWriteSame) {
   rados_aio_release(my_completion3);
 }
 
-TEST(LibRadosAio, RoundTripWriteSamePP) {
-  AioTestDataPP test_data;
-  ASSERT_EQ("", test_data.init());
-  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  AioCompletion *my_completion_null = NULL;
-  ASSERT_NE(my_completion, my_completion_null);
-  char full[128];
-  memset(full, 0xcc, sizeof(full));
-  bufferlist bl1;
-  bl1.append(full, sizeof(full));
-  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
-                                          bl1, sizeof(full), 0));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion->get_return_value());
-  /* write the same buf four times */
-  char buf[32];
-  size_t ws_write_len = sizeof(full);
-  memset(buf, 0xdd, sizeof(buf));
-  bufferlist bl2;
-  bl2.append(buf, sizeof(buf));
-  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  ASSERT_NE(my_completion2, my_completion_null);
-  ASSERT_EQ(0, test_data.m_ioctx.aio_writesame("foo", my_completion2, bl2,
-                                              ws_write_len, 0));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion2->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion2->get_return_value());
-  bufferlist bl3;
-  AioCompletion *my_completion3 = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  ASSERT_NE(my_completion3, my_completion_null);
-  ASSERT_EQ(0, test_data.m_ioctx.aio_read("foo", my_completion3,
-                                         &bl3, sizeof(full), 0));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion3->wait_for_complete());
-  }
-  ASSERT_EQ((int)sizeof(full), my_completion3->get_return_value());
-  ASSERT_EQ(sizeof(full), bl3.length());
-  for (char *cmp = bl3.c_str(); cmp < bl3.c_str() + bl3.length();
-                                                       cmp += sizeof(buf)) {
-    ASSERT_EQ(0, memcmp(cmp, buf, sizeof(buf)));
-  }
-  delete my_completion;
-  delete my_completion2;
-  delete my_completion3;
-}
-
-TEST(LibRadosAio, RoundTripWriteSamePP2)
-{
-  Rados cluster;
-  std::string pool_name = get_temp_pool_name();
-  ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
-  IoCtx ioctx;
-  cluster.ioctx_create(pool_name.c_str(), ioctx);
-
-  boost::scoped_ptr<AioCompletion>
-                       wr_cmpl(cluster.aio_create_completion(0, 0, 0));
-  ObjectWriteOperation op;
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl;
-  bl.append(buf, sizeof(buf));
-
-  op.writesame(0, sizeof(buf) * 4, bl);
-  op.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
-  ioctx.aio_operate("test_obj", wr_cmpl.get(), &op);
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, wr_cmpl->wait_for_complete());
-  }
-  EXPECT_EQ(0, wr_cmpl->get_return_value());
-
-  boost::scoped_ptr<AioCompletion>
-                       rd_cmpl(cluster.aio_create_completion(0, 0, 0));
-  char *cmp;
-  char full[sizeof(buf) * 4];
-  memset(full, 0, sizeof(full));
-  bufferlist fl;
-  fl.append(full, sizeof(full));
-  ObjectReadOperation op1;
-  op1.read(0, sizeof(full), &fl, NULL);
-  op1.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
-  ioctx.aio_operate("test_obj", rd_cmpl.get(), &op1, 0);
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, rd_cmpl->wait_for_complete());
-  }
-  EXPECT_EQ(0, rd_cmpl->get_return_value());
-  for (cmp = fl.c_str(); cmp < fl.c_str() + fl.length(); cmp += sizeof(buf)) {
-    ASSERT_EQ(0, memcmp(cmp, buf, sizeof(buf)));
-  }
-
-  ioctx.remove("test_obj");
-  destroy_one_pool_pp(pool_name, cluster);
-}
-
 TEST(LibRadosAio, SimpleStat) {
   AioTestData test_data;
   rados_completion_t my_completion;
@@ -1742,41 +776,6 @@ TEST(LibRadosAio, SimpleStat) {
   rados_aio_release(my_completion2);
 }
 
-TEST(LibRadosAio, SimpleStatPP) {
-  AioTestDataPP test_data;
-  ASSERT_EQ("", test_data.init());
-  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  AioCompletion *my_completion_null = NULL;
-  ASSERT_NE(my_completion, my_completion_null);
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
-                                          bl1, sizeof(buf), 0));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion->get_return_value());
-  uint64_t psize;
-  time_t pmtime;
-  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  ASSERT_NE(my_completion2, my_completion_null);
-  ASSERT_EQ(0, test_data.m_ioctx.aio_stat("foo", my_completion2,
-                                       &psize, &pmtime));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion2->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion2->get_return_value());
-  ASSERT_EQ(sizeof(buf), psize);
-  delete my_completion;
-  delete my_completion2;
-}
-
 TEST(LibRadosAio, SimpleStatNS) {
   AioTestData test_data;
   rados_completion_t my_completion;
@@ -1837,41 +836,6 @@ TEST(LibRadosAio, SimpleStatNS) {
   rados_aio_release(my_completion3);
 }
 
-TEST(LibRadosAio, SimpleStatPPNS) {
-  AioTestDataPP test_data;
-  ASSERT_EQ("", test_data.init());
-  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  AioCompletion *my_completion_null = NULL;
-  ASSERT_NE(my_completion, my_completion_null);
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
-                                          bl1, sizeof(buf), 0));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion->get_return_value());
-  uint64_t psize;
-  time_t pmtime;
-  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  ASSERT_NE(my_completion2, my_completion_null);
-  ASSERT_EQ(0, test_data.m_ioctx.aio_stat("foo", my_completion2,
-                                       &psize, &pmtime));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion2->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion2->get_return_value());
-  ASSERT_EQ(sizeof(buf), psize);
-  delete my_completion;
-  delete my_completion2;
-}
-
 TEST(LibRadosAio, StatRemove) {
   AioTestData test_data;
   rados_completion_t my_completion;
@@ -1927,65 +891,6 @@ TEST(LibRadosAio, StatRemove) {
   rados_aio_release(my_completion4);
 }
 
-TEST(LibRadosAio, StatRemovePP) {
-  AioTestDataPP test_data;
-  ASSERT_EQ("", test_data.init());
-  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  AioCompletion *my_completion_null = NULL;
-  ASSERT_NE(my_completion, my_completion_null);
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
-                                          bl1, sizeof(buf), 0));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion->get_return_value());
-  uint64_t psize;
-  time_t pmtime;
-  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  ASSERT_NE(my_completion2, my_completion_null);
-  ASSERT_EQ(0, test_data.m_ioctx.aio_stat("foo", my_completion2,
-                                       &psize, &pmtime));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion2->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion2->get_return_value());
-  ASSERT_EQ(sizeof(buf), psize);
-  uint64_t psize2;
-  time_t pmtime2;
-  AioCompletion *my_completion3 = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  ASSERT_NE(my_completion3, my_completion_null);
-  ASSERT_EQ(0, test_data.m_ioctx.aio_remove("foo", my_completion3));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion3->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion3->get_return_value());
-
-  AioCompletion *my_completion4 = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  ASSERT_NE(my_completion4, my_completion_null);
-  ASSERT_EQ(0, test_data.m_ioctx.aio_stat("foo", my_completion4,
-                                       &psize2, &pmtime2));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion4->wait_for_complete());
-  }
-  ASSERT_EQ(-ENOENT, my_completion4->get_return_value());
-  delete my_completion;
-  delete my_completion2;
-  delete my_completion3;
-  delete my_completion4;
-}
-
 TEST(LibRadosAio, ExecuteClass) {
   AioTestData test_data;
   rados_completion_t my_completion;
@@ -2016,245 +921,10 @@ TEST(LibRadosAio, ExecuteClass) {
   rados_aio_release(my_completion2);
 }
 
-TEST(LibRadosAio, ExecuteClassPP) {
-  AioTestDataPP test_data;
-  ASSERT_EQ("", test_data.init());
-  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  AioCompletion *my_completion_null = NULL;
-  ASSERT_NE(my_completion, my_completion_null);
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
-                                          bl1, sizeof(buf), 0));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion->get_return_value());
-  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  ASSERT_NE(my_completion2, my_completion_null);
-  bufferlist in, out;
-  ASSERT_EQ(0, test_data.m_ioctx.aio_exec("foo", my_completion2,
-                                         "hello", "say_hello", in, &out));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion2->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion2->get_return_value());
-  ASSERT_EQ(std::string("Hello, world!"), std::string(out.c_str(), out.length()));
-  delete my_completion;
-  delete my_completion2;
-}
-
 using std::string;
 using std::map;
 using std::set;
 
-TEST(LibRadosAio, OmapPP) {
-  Rados cluster;
-  std::string pool_name = get_temp_pool_name();
-  ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
-  IoCtx ioctx;
-  cluster.ioctx_create(pool_name.c_str(), ioctx);
-
-  string header_str = "baz";
-  bufferptr bp(header_str.c_str(), header_str.size() + 1);
-  bufferlist header_to_set;
-  header_to_set.push_back(bp);
-  map<string, bufferlist> to_set;
-  {
-    boost::scoped_ptr<AioCompletion> my_completion(cluster.aio_create_completion(0, 0, 0));
-    ObjectWriteOperation op;
-    to_set["foo"] = header_to_set;
-    to_set["foo2"] = header_to_set;
-    to_set["qfoo3"] = header_to_set;
-    op.omap_set(to_set);
-
-    op.omap_set_header(header_to_set);
-
-    ioctx.aio_operate("test_obj", my_completion.get(), &op);
-    {
-      TestAlarm alarm;
-      ASSERT_EQ(0, my_completion->wait_for_complete());
-    }
-    EXPECT_EQ(0, my_completion->get_return_value());
-  }
-
-  {
-    boost::scoped_ptr<AioCompletion> my_completion(cluster.aio_create_completion(0, 0, 0));
-    ObjectReadOperation op;
-    map<string, pair<bufferlist, int> > assertions;
-    bufferlist val;
-    val.append(string("bar"));
-    assertions["foo"] = pair<bufferlist, int>(val, CEPH_OSD_CMPXATTR_OP_EQ);
-
-    int r;
-    op.omap_cmp(assertions, &r);
-
-    ioctx.aio_operate("test_obj", my_completion.get(), &op, 0);
-    {
-      TestAlarm alarm;
-      ASSERT_EQ(0, my_completion->wait_for_complete());
-    }
-    EXPECT_EQ(-ECANCELED, my_completion->get_return_value());
-    ASSERT_EQ(-ECANCELED, r);
-  }
-
-  {
-    boost::scoped_ptr<AioCompletion> my_completion(cluster.aio_create_completion(0, 0, 0));
-    ObjectReadOperation op;
-
-    set<string> set_got;
-    map<string, bufferlist> map_got;
-
-    set<string> to_get;
-    map<string, bufferlist> got3;
-
-    map<string, bufferlist> got4;
-
-    bufferlist header;
-
-    op.omap_get_keys2("", 1, &set_got, nullptr, 0);
-    op.omap_get_vals2("foo", 1, &map_got, nullptr, 0);
-
-    to_get.insert("foo");
-    to_get.insert("qfoo3");
-    op.omap_get_vals_by_keys(to_get, &got3, 0);
-
-    op.omap_get_header(&header, 0);
-
-    op.omap_get_vals2("foo2", "q", 1, &got4, nullptr, 0);
-
-    ioctx.aio_operate("test_obj", my_completion.get(), &op, 0);
-    {
-      TestAlarm alarm;
-      ASSERT_EQ(0, my_completion->wait_for_complete());
-    }
-    EXPECT_EQ(0, my_completion->get_return_value());
-
-    ASSERT_EQ(header.length(), header_to_set.length());
-    ASSERT_EQ(set_got.size(), (unsigned)1);
-    ASSERT_EQ(*set_got.begin(), "foo");
-    ASSERT_EQ(map_got.size(), (unsigned)1);
-    ASSERT_EQ(map_got.begin()->first, "foo2");
-    ASSERT_EQ(got3.size(), (unsigned)2);
-    ASSERT_EQ(got3.begin()->first, "foo");
-    ASSERT_EQ(got3.rbegin()->first, "qfoo3");
-    ASSERT_EQ(got4.size(), (unsigned)1);
-    ASSERT_EQ(got4.begin()->first, "qfoo3");
-  }
-
-  {
-    boost::scoped_ptr<AioCompletion> my_completion(cluster.aio_create_completion(0, 0, 0));
-    ObjectWriteOperation op;
-    set<string> to_remove;
-    to_remove.insert("foo2");
-    op.omap_rm_keys(to_remove);
-    ioctx.aio_operate("test_obj", my_completion.get(), &op);
-    {
-      TestAlarm alarm;
-      ASSERT_EQ(0, my_completion->wait_for_complete());
-    }
-    EXPECT_EQ(0, my_completion->get_return_value());
-  }
-
-  {
-    boost::scoped_ptr<AioCompletion> my_completion(cluster.aio_create_completion(0, 0, 0));
-    ObjectReadOperation op;
-
-    set<string> set_got;
-    op.omap_get_keys2("", -1, &set_got, nullptr, 0);
-    ioctx.aio_operate("test_obj", my_completion.get(), &op, 0);
-    {
-      TestAlarm alarm;
-      ASSERT_EQ(0, my_completion->wait_for_complete());
-    }
-    EXPECT_EQ(0, my_completion->get_return_value());
-    ASSERT_EQ(set_got.size(), (unsigned)2);
-  }
-
-  {
-    boost::scoped_ptr<AioCompletion> my_completion(cluster.aio_create_completion(0, 0, 0));
-    ObjectWriteOperation op;
-    op.omap_clear();
-    ioctx.aio_operate("test_obj", my_completion.get(), &op);
-    {
-      TestAlarm alarm;
-      ASSERT_EQ(0, my_completion->wait_for_complete());
-    }
-    EXPECT_EQ(0, my_completion->get_return_value());
-  }
-
-  {
-    boost::scoped_ptr<AioCompletion> my_completion(cluster.aio_create_completion(0, 0, 0));
-    ObjectReadOperation op;
-
-    set<string> set_got;
-    op.omap_get_keys2("", -1, &set_got, nullptr, 0);
-    ioctx.aio_operate("test_obj", my_completion.get(), &op, 0);
-    {
-      TestAlarm alarm;
-      ASSERT_EQ(0, my_completion->wait_for_complete());
-    }
-    EXPECT_EQ(0, my_completion->get_return_value());
-    ASSERT_EQ(set_got.size(), (unsigned)0);
-  }
-
-  // omap_clear clears header *and* keys
-  {
-    boost::scoped_ptr<AioCompletion> my_completion(cluster.aio_create_completion(0, 0, 0));
-    ObjectWriteOperation op;
-    bufferlist bl;
-    bl.append("some data");
-    map<string,bufferlist> to_set;
-    to_set["foo"] = bl;
-    to_set["foo2"] = bl;
-    to_set["qfoo3"] = bl;
-    op.omap_set(to_set);
-    op.omap_set_header(bl);
-    ioctx.aio_operate("foo3", my_completion.get(), &op);
-    {
-      TestAlarm alarm;
-      ASSERT_EQ(0, my_completion->wait_for_complete());
-    }
-    EXPECT_EQ(0, my_completion->get_return_value());
-  }
-  {
-    boost::scoped_ptr<AioCompletion> my_completion(cluster.aio_create_completion(0, 0, 0));
-    ObjectWriteOperation op;
-    op.omap_clear();
-    ioctx.aio_operate("foo3", my_completion.get(), &op);
-    {
-      TestAlarm alarm;
-      ASSERT_EQ(0, my_completion->wait_for_complete());
-    }
-    EXPECT_EQ(0, my_completion->get_return_value());
-  }
-  {
-    boost::scoped_ptr<AioCompletion> my_completion(cluster.aio_create_completion(0, 0, 0));
-    ObjectReadOperation op;
-    set<string> set_got;
-    bufferlist hdr;
-    op.omap_get_keys2("", -1, &set_got, nullptr, 0);
-    op.omap_get_header(&hdr, NULL);
-    ioctx.aio_operate("foo3", my_completion.get(), &op, 0);
-    {
-      TestAlarm alarm;
-      ASSERT_EQ(0, my_completion->wait_for_complete());
-    }
-    EXPECT_EQ(0, my_completion->get_return_value());
-    ASSERT_EQ(set_got.size(), (unsigned)0);
-    ASSERT_EQ(hdr.length(), 0u);
-  }
-
-  ioctx.remove("test_obj");
-  destroy_one_pool_pp(pool_name, cluster);
-}
-
 TEST(LibRadosAio, MultiWrite) {
   AioTestData test_data;
   rados_completion_t my_completion, my_completion2, my_completion3;
@@ -2301,59 +971,6 @@ TEST(LibRadosAio, MultiWrite) {
   rados_aio_release(my_completion3);
 }
 
-TEST(LibRadosAio, MultiWritePP) {
-  AioTestDataPP test_data;
-  ASSERT_EQ("", test_data.init());
-  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  AioCompletion *my_completion_null = NULL;
-  ASSERT_NE(my_completion, my_completion_null);
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
-                                          bl1, sizeof(buf), 0));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion->get_return_value());
-
-  char buf2[64];
-  memset(buf2, 0xdd, sizeof(buf2));
-  bufferlist bl2;
-  bl2.append(buf2, sizeof(buf2));
-  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  ASSERT_NE(my_completion2, my_completion_null);
-  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion2,
-                                          bl2, sizeof(buf2), sizeof(buf)));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion2->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion2->get_return_value());
-
-  bufferlist bl3;
-  AioCompletion *my_completion3 = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  ASSERT_NE(my_completion3, my_completion_null);
-  ASSERT_EQ(0, test_data.m_ioctx.aio_read("foo", my_completion3,
-                                         &bl3, (sizeof(buf) + sizeof(buf2) * 3), 0));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion3->wait_for_complete());
-  }
-  ASSERT_EQ((int)(sizeof(buf) + sizeof(buf2)), my_completion3->get_return_value());
-  ASSERT_EQ(sizeof(buf) + sizeof(buf2), bl3.length());
-  ASSERT_EQ(0, memcmp(bl3.c_str(), buf, sizeof(buf)));
-  ASSERT_EQ(0, memcmp(bl3.c_str() + sizeof(buf), buf2, sizeof(buf2)));
-  delete my_completion;
-  delete my_completion2;
-  delete my_completion3;
-}
-
 TEST(LibRadosAio, AioUnlock) {
   AioTestData test_data;
   ASSERT_EQ("", test_data.init());
@@ -2370,23 +987,6 @@ TEST(LibRadosAio, AioUnlock) {
   ASSERT_EQ(0, rados_lock_exclusive(test_data.m_ioctx, "foo", "TestLock", "Cookie", "", NULL,  0));
 }
 
-TEST(LibRadosAio, AioUnlockPP) {
-  AioTestDataPP test_data;
-  ASSERT_EQ("", test_data.init());
-  ASSERT_EQ(0, test_data.m_ioctx.lock_exclusive("foo", "TestLock", "Cookie", "", NULL, 0));
-  boost::scoped_ptr<AioCompletion> my_completion
-    (test_data.m_cluster.aio_create_completion
-     (nullptr, nullptr, nullptr));
-  ASSERT_EQ(0, test_data.m_ioctx.aio_unlock("foo", "TestLock", "Cookie", my_completion.get()));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion->get_return_value());
-  bufferlist bl2;
-  ASSERT_EQ(0, test_data.m_ioctx.lock_exclusive("foo", "TestLock", "Cookie", "", NULL, 0));
-}
-
 // EC test cases
 class AioTestDataEC
 {
@@ -2433,49 +1033,6 @@ public:
   bool m_init;
 };
 
-class AioTestDataECPP
-{
-public:
-  AioTestDataECPP()
-    : m_init(false)
-  {
-  }
-
-  ~AioTestDataECPP()
-  {
-    if (m_init) {
-      m_ioctx.close();
-      destroy_one_ec_pool_pp(m_pool_name, m_cluster);
-    }
-  }
-
-  std::string init()
-  {
-    int ret;
-    m_pool_name = get_temp_pool_name();
-    std::string err = create_one_ec_pool_pp(m_pool_name, m_cluster);
-    if (!err.empty()) {
-      ostringstream oss;
-      oss << "create_one_ec_pool(" << m_pool_name << ") failed: error " << err;
-      return oss.str();
-    }
-    ret = m_cluster.ioctx_create(m_pool_name.c_str(), m_ioctx);
-    if (ret) {
-      destroy_one_ec_pool_pp(m_pool_name, m_cluster);
-      ostringstream oss;
-      oss << "rados_ioctx_create failed: error " << ret;
-      return oss.str();
-    }
-    m_init = true;
-    return "";
-  }
-
-  Rados m_cluster;
-  IoCtx m_ioctx;
-  std::string m_pool_name;
-  bool m_init;
-};
-
 TEST(LibRadosAioEC, SimpleWrite) {
   AioTestDataEC test_data;
   rados_completion_t my_completion;
@@ -2507,45 +1064,6 @@ TEST(LibRadosAioEC, SimpleWrite) {
   ASSERT_EQ(0, rados_aio_get_return_value(my_completion2));
 }
 
-TEST(LibRadosAioEC, SimpleWritePP) {
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  {
-  AioTestDataECPP test_data;
-  ASSERT_EQ("", test_data.init());
-  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  AioCompletion *my_completion_null = NULL;
-  ASSERT_NE(my_completion, my_completion_null);
-  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo",
-                              my_completion, bl1, sizeof(buf), 0));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion->get_return_value());
-  delete my_completion;
-  }
-
-  {
-  AioTestDataECPP test_data;
-  ASSERT_EQ("", test_data.init());
-  test_data.m_ioctx.set_namespace("nspace");
-  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo",
-                              my_completion, bl1, sizeof(buf), 0));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion->get_return_value());
-  delete my_completion;
-  }
-}
-
 TEST(LibRadosAioEC, WaitForSafe) {
   AioTestDataEC test_data;
   rados_completion_t my_completion;
@@ -2562,25 +1080,6 @@ TEST(LibRadosAioEC, WaitForSafe) {
   rados_aio_release(my_completion);
 }
 
-TEST(LibRadosAioEC, WaitForSafePP) {
-  AioTestDataECPP test_data;
-  ASSERT_EQ("", test_data.init());
-  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  AioCompletion *my_completion_null = NULL;
-  ASSERT_NE(my_completion, my_completion_null);
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo",
-                              my_completion, bl1, sizeof(buf), 0));
-  TestAlarm alarm;
-  ASSERT_EQ(0, my_completion->wait_for_safe());
-  ASSERT_EQ(0, my_completion->get_return_value());
-  delete my_completion;
-}
-
 TEST(LibRadosAioEC, RoundTrip) {
   AioTestDataEC test_data;
   rados_completion_t my_completion;
@@ -2633,165 +1132,16 @@ TEST(LibRadosAioEC, RoundTrip2) {
   rados_completion_t my_completion2;
   ASSERT_EQ(0, rados_aio_create_completion(nullptr,
              nullptr, nullptr, &my_completion2));
-  ASSERT_EQ(0, rados_aio_read(test_data.m_ioctx, "foo",
-                             my_completion2, buf2, sizeof(buf2), 0));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion2));
-  }
-  ASSERT_EQ((int)sizeof(buf), rados_aio_get_return_value(my_completion2));
-  ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf)));
-  rados_aio_release(my_completion);
-  rados_aio_release(my_completion2);
-}
-
-TEST(LibRadosAioEC, RoundTripPP) {
-  AioTestDataECPP test_data;
-  ASSERT_EQ("", test_data.init());
-  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  AioCompletion *my_completion_null = NULL;
-  ASSERT_NE(my_completion, my_completion_null);
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
-                                          bl1, sizeof(buf), 0));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion->get_return_value());
-  bufferlist bl2;
-  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  ASSERT_NE(my_completion2, my_completion_null);
-  ASSERT_EQ(0, test_data.m_ioctx.aio_read("foo",
-                             my_completion2, &bl2, sizeof(buf), 0));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion2->wait_for_complete());
-  }
-  ASSERT_EQ((int)sizeof(buf), my_completion2->get_return_value());
-  ASSERT_EQ(sizeof(buf), bl2.length());
-  ASSERT_EQ(0, memcmp(buf, bl2.c_str(), sizeof(buf)));
-  delete my_completion;
-  delete my_completion2;
-}
-
-TEST(LibRadosAioEC, RoundTripPP2) {
-  AioTestDataECPP test_data;
-  ASSERT_EQ("", test_data.init());
-  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  AioCompletion *my_completion_null = NULL;
-  ASSERT_NE(my_completion, my_completion_null);
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
-                                          bl1, sizeof(buf), 0));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion->get_return_value());
-  bufferlist bl2;
-  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  ASSERT_NE(my_completion2, my_completion_null);
-  ASSERT_EQ(0, test_data.m_ioctx.aio_read("foo",
-                             my_completion2, &bl2, sizeof(buf), 0));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion2->wait_for_safe());
-    ASSERT_EQ(0, my_completion2->wait_for_complete());
-  }
-  ASSERT_EQ((int)sizeof(buf), my_completion2->get_return_value());
-  ASSERT_EQ(sizeof(buf), bl2.length());
-  ASSERT_EQ(0, memcmp(buf, bl2.c_str(), sizeof(buf)));
-  delete my_completion;
-  delete my_completion2;
-}
-
-//using ObjectWriteOperation/ObjectReadOperation with iohint
-TEST(LibRadosAioEC, RoundTripPP3)
-{
-  Rados cluster;
-  std::string pool_name = get_temp_pool_name();
-  ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
-  IoCtx ioctx;
-  cluster.ioctx_create(pool_name.c_str(), ioctx);
-
-  boost::scoped_ptr<AioCompletion> my_completion1(cluster.aio_create_completion(0, 0, 0));
-  ObjectWriteOperation op;
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl;
-  bl.append(buf);
-
-  op.write(0, bl);
-  op.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
-  ioctx.aio_operate("test_obj", my_completion1.get(), &op);
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion1->wait_for_complete());
-  }
-  EXPECT_EQ(0, my_completion1->get_return_value());
-
-  boost::scoped_ptr<AioCompletion> my_completion2(cluster.aio_create_completion(0, 0, 0));
-  bl.clear();
-  ObjectReadOperation op1;
-  op1.read(0, sizeof(buf), &bl, NULL);
-  op1.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED|LIBRADOS_OP_FLAG_FADVISE_RANDOM);
-  ioctx.aio_operate("test_obj", my_completion2.get(), &op1, 0);
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion2->wait_for_complete());
-  }
-  EXPECT_EQ(0, my_completion2->get_return_value());
-  ASSERT_EQ(0, memcmp(buf, bl.c_str(), sizeof(buf)));
-
-  ioctx.remove("test_obj");
-  destroy_one_pool_pp(pool_name, cluster);
-}
-
-TEST(LibRadosAioEC, RoundTripSparseReadPP) {
-  AioTestDataECPP test_data;
-  ASSERT_EQ("", test_data.init());
-  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  AioCompletion *my_completion_null = NULL;
-  ASSERT_NE(my_completion, my_completion_null);
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
-                                          bl1, sizeof(buf), 0));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion->get_return_value());
-
-  map<uint64_t, uint64_t> extents;
-  bufferlist bl2;
-  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  ASSERT_NE(my_completion2, my_completion_null);
-  ASSERT_EQ(0, test_data.m_ioctx.aio_sparse_read("foo",
-                             my_completion2, &extents, &bl2, sizeof(buf), 0));
+  ASSERT_EQ(0, rados_aio_read(test_data.m_ioctx, "foo",
+                             my_completion2, buf2, sizeof(buf2), 0));
   {
     TestAlarm alarm;
-    ASSERT_EQ(0, my_completion2->wait_for_complete());
+    ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion2));
   }
-  ASSERT_EQ(0, my_completion2->get_return_value());
-  assert_eq_sparse(bl1, extents, bl2);
-  delete my_completion;
-  delete my_completion2;
+  ASSERT_EQ((int)sizeof(buf), rados_aio_get_return_value(my_completion2));
+  ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf)));
+  rados_aio_release(my_completion);
+  rados_aio_release(my_completion2);
 }
 
 TEST(LibRadosAioEC, RoundTripAppend) {
@@ -2864,81 +1214,6 @@ TEST(LibRadosAioEC, RoundTripAppend) {
   delete[] buf3;
 }
 
-TEST(LibRadosAioEC, RoundTripAppendPP) {
-  AioTestDataECPP test_data;
-  ASSERT_EQ("", test_data.init());
-  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  AioCompletion *my_completion_null = NULL;
-  ASSERT_NE(my_completion, my_completion_null);
-  bool requires;
-  ASSERT_EQ(0, test_data.m_ioctx.pool_requires_alignment2(&requires));
-  ASSERT_TRUE(requires);
-  uint64_t alignment;
-  ASSERT_EQ(0, test_data.m_ioctx.pool_required_alignment2(&alignment));
-  ASSERT_NE((unsigned)0, alignment);
-  int bsize = alignment;
-  char *buf = (char *)new char[bsize];
-  memset(buf, 0xcc, bsize);
-  bufferlist bl1;
-  bl1.append(buf, bsize);
-  ASSERT_EQ(0, test_data.m_ioctx.aio_append("foo", my_completion,
-                                           bl1, bsize));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion->get_return_value());
-
-  int hbsize = bsize / 2;
-  char *buf2 = (char *)new char[hbsize];
-  memset(buf2, 0xdd, hbsize);
-  bufferlist bl2;
-  bl2.append(buf2, hbsize);
-  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  ASSERT_NE(my_completion2, my_completion_null);
-  ASSERT_EQ(0, test_data.m_ioctx.aio_append("foo", my_completion2,
-                                           bl2, hbsize));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion2->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion2->get_return_value());
-
-  AioCompletion *my_completion3 = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  ASSERT_NE(my_completion3, my_completion_null);
-  ASSERT_EQ(0, test_data.m_ioctx.aio_append("foo", my_completion3,
-                                           bl2, hbsize));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion3->wait_for_complete());
-  }
-  EXPECT_EQ(-EOPNOTSUPP, my_completion3->get_return_value());
-
-  bufferlist bl3;
-  AioCompletion *my_completion4 = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  ASSERT_NE(my_completion4, my_completion_null);
-  ASSERT_EQ(0, test_data.m_ioctx.aio_read("foo",
-                             my_completion4, &bl3, bsize * 3, 0));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion4->wait_for_complete());
-  }
-  int tbsize = bsize + hbsize;
-  ASSERT_EQ(tbsize, my_completion4->get_return_value());
-  ASSERT_EQ((unsigned)tbsize, bl3.length());
-  ASSERT_EQ(0, memcmp(bl3.c_str(), buf, bsize));
-  ASSERT_EQ(0, memcmp(bl3.c_str() + bsize, buf2, hbsize));
-  delete my_completion;
-  delete my_completion2;
-  delete my_completion3;
-  delete[] buf;
-  delete[] buf2;
-}
-
 TEST(LibRadosAioEC, IsComplete) {
   AioTestDataEC test_data;
   rados_completion_t my_completion;
@@ -2978,48 +1253,6 @@ TEST(LibRadosAioEC, IsComplete) {
   rados_aio_release(my_completion2);
 }
 
-TEST(LibRadosAioEC, IsCompletePP) {
-  AioTestDataECPP test_data;
-  ASSERT_EQ("", test_data.init());
-  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  AioCompletion *my_completion_null = NULL;
-  ASSERT_NE(my_completion, my_completion_null);
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
-                                          bl1, sizeof(buf), 0));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion->get_return_value());
-  bufferlist bl2;
-  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  ASSERT_NE(my_completion2, my_completion_null);
-  ASSERT_EQ(0, test_data.m_ioctx.aio_read("foo", my_completion2,
-                                         &bl2, sizeof(buf), 0));
-  {
-    TestAlarm alarm;
-
-    // Busy-wait until the AIO completes.
-    // Normally we wouldn't do this, but we want to test is_complete.
-    while (true) {
-      int is_complete = my_completion2->is_complete();
-      if (is_complete)
-       break;
-    }
-  }
-  ASSERT_EQ((int)sizeof(buf), my_completion2->get_return_value());
-  ASSERT_EQ(sizeof(buf), bl2.length());
-  ASSERT_EQ(0, memcmp(buf, bl2.c_str(), sizeof(buf)));
-  delete my_completion;
-  delete my_completion2;
-}
-
 TEST(LibRadosAioEC, IsSafe) {
   AioTestDataEC test_data;
   rados_completion_t my_completion;
@@ -3059,48 +1292,6 @@ TEST(LibRadosAioEC, IsSafe) {
   rados_aio_release(my_completion2);
 }
 
-TEST(LibRadosAioEC, IsSafePP) {
-  AioTestDataECPP test_data;
-  ASSERT_EQ("", test_data.init());
-  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  AioCompletion *my_completion_null = NULL;
-  ASSERT_NE(my_completion, my_completion_null);
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
-                                          bl1, sizeof(buf), 0));
-  {
-    TestAlarm alarm;
-
-    // Busy-wait until the AIO completes.
-    // Normally we wouldn't do this, but we want to test rados_aio_is_safe.
-    while (true) {
-      int is_safe = my_completion->is_safe();
-      if (is_safe)
-       break;
-    }
-  }
-  ASSERT_EQ(0, my_completion->get_return_value());
-  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  bufferlist bl2;
-  ASSERT_NE(my_completion2, my_completion_null);
-  ASSERT_EQ(0, test_data.m_ioctx.aio_read("foo", my_completion2,
-                                         &bl2, sizeof(buf), 0));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion2->wait_for_complete());
-  }
-  ASSERT_EQ((int)sizeof(buf), my_completion2->get_return_value());
-  ASSERT_EQ(sizeof(buf), bl2.length());
-  ASSERT_EQ(0, memcmp(buf, bl2.c_str(), sizeof(buf)));
-  delete my_completion;
-  delete my_completion2;
-}
-
 TEST(LibRadosAioEC, ReturnValue) {
   AioTestDataEC test_data;
   rados_completion_t my_completion;
@@ -3119,24 +1310,6 @@ TEST(LibRadosAioEC, ReturnValue) {
   rados_aio_release(my_completion);
 }
 
-TEST(LibRadosAioEC, ReturnValuePP) {
-  AioTestDataECPP test_data;
-  ASSERT_EQ("", test_data.init());
-  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  AioCompletion *my_completion_null = NULL;
-  ASSERT_NE(my_completion, my_completion_null);
-  bufferlist bl1;
-  ASSERT_EQ(0, test_data.m_ioctx.aio_read("nonexistent",
-                              my_completion, &bl1, 128, 0));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion->wait_for_complete());
-  }
-  ASSERT_EQ(-ENOENT, my_completion->get_return_value());
-  delete my_completion;
-}
-
 TEST(LibRadosAioEC, Flush) {
   AioTestDataEC test_data;
   rados_completion_t my_completion;
@@ -3166,38 +1339,6 @@ TEST(LibRadosAioEC, Flush) {
   rados_aio_release(my_completion2);
 }
 
-TEST(LibRadosAioEC, FlushPP) {
-  AioTestDataECPP test_data;
-  ASSERT_EQ("", test_data.init());
-  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  AioCompletion *my_completion_null = NULL;
-  ASSERT_NE(my_completion, my_completion_null);
-  char buf[128];
-  memset(buf, 0xee, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
-                                          bl1, sizeof(buf), 0));
-  ASSERT_EQ(0, test_data.m_ioctx.aio_flush());
-  ASSERT_EQ(0, my_completion->get_return_value());
-  bufferlist bl2;
-  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  ASSERT_NE(my_completion2, my_completion_null);
-  ASSERT_EQ(0, test_data.m_ioctx.aio_read("foo", my_completion2,
-                                         &bl2, sizeof(buf), 0));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion2->wait_for_complete());
-  }
-  ASSERT_EQ((int)sizeof(buf), my_completion2->get_return_value());
-  ASSERT_EQ(sizeof(buf), bl2.length());
-  ASSERT_EQ(0, memcmp(buf, bl2.c_str(), sizeof(buf)));
-  delete my_completion;
-  delete my_completion2;
-}
-
 TEST(LibRadosAioEC, FlushAsync) {
   AioTestDataEC test_data;
   rados_completion_t my_completion;
@@ -3239,50 +1380,6 @@ TEST(LibRadosAioEC, FlushAsync) {
   rados_aio_release(flush_completion);
 }
 
-TEST(LibRadosAioEC, FlushAsyncPP) {
-  AioTestDataECPP test_data;
-  ASSERT_EQ("", test_data.init());
-  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  AioCompletion *flush_completion =
-      test_data.m_cluster.aio_create_completion(NULL, NULL, NULL);
-  AioCompletion *my_completion_null = NULL;
-  ASSERT_NE(my_completion, my_completion_null);
-  char buf[128];
-  memset(buf, 0xee, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
-                                          bl1, sizeof(buf), 0));
-  ASSERT_EQ(0, test_data.m_ioctx.aio_flush_async(flush_completion));
-  {
-      TestAlarm alarm;
-      ASSERT_EQ(0, flush_completion->wait_for_complete());
-      ASSERT_EQ(0, flush_completion->wait_for_safe());
-  }
-  ASSERT_EQ(1, my_completion->is_complete());
-  ASSERT_EQ(1, my_completion->is_safe());
-  ASSERT_EQ(1, flush_completion->is_complete());
-  ASSERT_EQ(1, flush_completion->is_safe());
-  ASSERT_EQ(0, my_completion->get_return_value());
-  bufferlist bl2;
-  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  ASSERT_NE(my_completion2, my_completion_null);
-  ASSERT_EQ(0, test_data.m_ioctx.aio_read("foo", my_completion2,
-                                         &bl2, sizeof(buf), 0));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion2->wait_for_complete());
-  }
-  ASSERT_EQ((int)sizeof(buf), my_completion2->get_return_value());
-  ASSERT_EQ(sizeof(buf), bl2.length());
-  ASSERT_EQ(0, memcmp(buf, bl2.c_str(), sizeof(buf)));
-  delete my_completion;
-  delete my_completion2;
-  delete flush_completion;
-}
-
 TEST(LibRadosAioEC, RoundTripWriteFull) {
   AioTestDataEC test_data;
   rados_completion_t my_completion, my_completion2, my_completion3;
@@ -3326,97 +1423,6 @@ TEST(LibRadosAioEC, RoundTripWriteFull) {
   rados_aio_release(my_completion3);
 }
 
-TEST(LibRadosAioEC, RoundTripWriteFullPP) {
-  AioTestDataECPP test_data;
-  ASSERT_EQ("", test_data.init());
-  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  AioCompletion *my_completion_null = NULL;
-  ASSERT_NE(my_completion, my_completion_null);
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
-                                          bl1, sizeof(buf), 0));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion->get_return_value());
-  char buf2[64];
-  memset(buf2, 0xdd, sizeof(buf2));
-  bufferlist bl2;
-  bl2.append(buf2, sizeof(buf2));
-  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  ASSERT_NE(my_completion2, my_completion_null);
-  ASSERT_EQ(0, test_data.m_ioctx.aio_write_full("foo", my_completion2, bl2));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion2->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion2->get_return_value());
-  bufferlist bl3;
-  AioCompletion *my_completion3 = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  ASSERT_NE(my_completion3, my_completion_null);
-  ASSERT_EQ(0, test_data.m_ioctx.aio_read("foo", my_completion3,
-                                         &bl3, sizeof(buf), 0));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion3->wait_for_complete());
-  }
-  ASSERT_EQ((int)sizeof(buf2), my_completion3->get_return_value());
-  ASSERT_EQ(sizeof(buf2), bl3.length());
-  ASSERT_EQ(0, memcmp(bl3.c_str(), buf2, sizeof(buf2)));
-  delete my_completion;
-  delete my_completion2;
-  delete my_completion3;
-}
-
-//using ObjectWriteOperation/ObjectReadOperation with iohint
-TEST(LibRadosAioEC, RoundTripWriteFullPP2)
-{
-  Rados cluster;
-  std::string pool_name = get_temp_pool_name();
-  ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
-  IoCtx ioctx;
-  cluster.ioctx_create(pool_name.c_str(), ioctx);
-
-  boost::scoped_ptr<AioCompletion> my_completion1(cluster.aio_create_completion(0, 0, 0));
-  ObjectWriteOperation op;
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl;
-  bl.append(buf);
-
-  op.write_full(bl);
-  op.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_NOCACHE);
-  ioctx.aio_operate("test_obj", my_completion1.get(), &op);
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion1->wait_for_complete());
-  }
-  EXPECT_EQ(0, my_completion1->get_return_value());
-
-  boost::scoped_ptr<AioCompletion> my_completion2(cluster.aio_create_completion(0, 0, 0));
-  bl.clear();
-  ObjectReadOperation op1;
-  op1.read(0, sizeof(buf), &bl, NULL);
-  op1.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_NOCACHE|LIBRADOS_OP_FLAG_FADVISE_RANDOM);
-  ioctx.aio_operate("test_obj", my_completion2.get(), &op1, 0);
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion2->wait_for_complete());
-  }
-  EXPECT_EQ(0, my_completion2->get_return_value());
-  ASSERT_EQ(0, memcmp(buf, bl.c_str(), sizeof(buf)));
-
-  ioctx.remove("test_obj");
-  destroy_one_pool_pp(pool_name, cluster);
-}
-
 TEST(LibRadosAioEC, SimpleStat) {
   AioTestDataEC test_data;
   rados_completion_t my_completion;
@@ -3449,40 +1455,6 @@ TEST(LibRadosAioEC, SimpleStat) {
   rados_aio_release(my_completion2);
 }
 
-TEST(LibRadosAioEC, SimpleStatPP) {
-  AioTestDataECPP test_data;
-  ASSERT_EQ("", test_data.init());
-  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  AioCompletion *my_completion_null = NULL;
-  ASSERT_NE(my_completion, my_completion_null);
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
-                                          bl1, sizeof(buf), 0));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion->get_return_value());
-  uint64_t psize;
-  time_t pmtime;
-  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  ASSERT_NE(my_completion2, my_completion_null);
-  ASSERT_EQ(0, test_data.m_ioctx.aio_stat("foo", my_completion2,
-                                       &psize, &pmtime));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion2->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion2->get_return_value());
-  ASSERT_EQ(sizeof(buf), psize);
-  delete my_completion;
-  delete my_completion2;
-}
 
 TEST(LibRadosAioEC, SimpleStatNS) {
   AioTestDataEC test_data;
@@ -3544,41 +1516,6 @@ TEST(LibRadosAioEC, SimpleStatNS) {
   rados_aio_release(my_completion3);
 }
 
-TEST(LibRadosAioEC, SimpleStatPPNS) {
-  AioTestDataECPP test_data;
-  ASSERT_EQ("", test_data.init());
-  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  AioCompletion *my_completion_null = NULL;
-  ASSERT_NE(my_completion, my_completion_null);
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
-                                          bl1, sizeof(buf), 0));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion->get_return_value());
-  uint64_t psize;
-  time_t pmtime;
-  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  ASSERT_NE(my_completion2, my_completion_null);
-  ASSERT_EQ(0, test_data.m_ioctx.aio_stat("foo", my_completion2,
-                                       &psize, &pmtime));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion2->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion2->get_return_value());
-  ASSERT_EQ(sizeof(buf), psize);
-  delete my_completion;
-  delete my_completion2;
-}
-
 TEST(LibRadosAioEC, StatRemove) {
   AioTestDataEC test_data;
   rados_completion_t my_completion;
@@ -3634,65 +1571,6 @@ TEST(LibRadosAioEC, StatRemove) {
   rados_aio_release(my_completion4);
 }
 
-TEST(LibRadosAioEC, StatRemovePP) {
-  AioTestDataECPP test_data;
-  ASSERT_EQ("", test_data.init());
-  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  AioCompletion *my_completion_null = NULL;
-  ASSERT_NE(my_completion, my_completion_null);
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
-                                          bl1, sizeof(buf), 0));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion->get_return_value());
-  uint64_t psize;
-  time_t pmtime;
-  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  ASSERT_NE(my_completion2, my_completion_null);
-  ASSERT_EQ(0, test_data.m_ioctx.aio_stat("foo", my_completion2,
-                                       &psize, &pmtime));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion2->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion2->get_return_value());
-  ASSERT_EQ(sizeof(buf), psize);
-  uint64_t psize2;
-  time_t pmtime2;
-  AioCompletion *my_completion3 = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  ASSERT_NE(my_completion3, my_completion_null);
-  ASSERT_EQ(0, test_data.m_ioctx.aio_remove("foo", my_completion3));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion3->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion3->get_return_value());
-
-  AioCompletion *my_completion4 = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  ASSERT_NE(my_completion4, my_completion_null);
-  ASSERT_EQ(0, test_data.m_ioctx.aio_stat("foo", my_completion4,
-                                       &psize2, &pmtime2));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion4->wait_for_complete());
-  }
-  ASSERT_EQ(-ENOENT, my_completion4->get_return_value());
-  delete my_completion;
-  delete my_completion2;
-  delete my_completion3;
-  delete my_completion4;
-}
-
 TEST(LibRadosAioEC, ExecuteClass) {
   AioTestDataEC test_data;
   rados_completion_t my_completion;
@@ -3724,73 +1602,6 @@ TEST(LibRadosAioEC, ExecuteClass) {
   rados_aio_release(my_completion2);
 }
 
-TEST(LibRadosAioEC, ExecuteClassPP) {
-  AioTestDataECPP test_data;
-  ASSERT_EQ("", test_data.init());
-  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  AioCompletion *my_completion_null = NULL;
-  ASSERT_NE(my_completion, my_completion_null);
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
-                                          bl1, sizeof(buf), 0));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion->get_return_value());
-  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  ASSERT_NE(my_completion2, my_completion_null);
-  bufferlist in, out;
-  ASSERT_EQ(0, test_data.m_ioctx.aio_exec("foo", my_completion2,
-                                         "hello", "say_hello", in, &out));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion2->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion2->get_return_value());
-  ASSERT_EQ(std::string("Hello, world!"), std::string(out.c_str(), out.length()));
-  delete my_completion;
-  delete my_completion2;
-}
-
-TEST(LibRadosAioEC, OmapPP) {
-  Rados cluster;
-  std::string pool_name = get_temp_pool_name();
-  ASSERT_EQ("", create_one_ec_pool_pp(pool_name, cluster));
-  IoCtx ioctx;
-  cluster.ioctx_create(pool_name.c_str(), ioctx);
-
-  string header_str = "baz";
-  bufferptr bp(header_str.c_str(), header_str.size() + 1);
-  bufferlist header_to_set;
-  header_to_set.push_back(bp);
-  map<string, bufferlist> to_set;
-  {
-    boost::scoped_ptr<AioCompletion> my_completion(cluster.aio_create_completion(0, 0, 0));
-    ObjectWriteOperation op;
-    to_set["foo"] = header_to_set;
-    to_set["foo2"] = header_to_set;
-    to_set["qfoo3"] = header_to_set;
-    op.omap_set(to_set);
-
-    op.omap_set_header(header_to_set);
-
-    ioctx.aio_operate("test_obj", my_completion.get(), &op);
-    {
-      TestAlarm alarm;
-      ASSERT_EQ(0, my_completion->wait_for_complete());
-    }
-    EXPECT_EQ(-EOPNOTSUPP, my_completion->get_return_value());
-  }
-  ioctx.remove("test_obj");
-  destroy_one_pool_pp(pool_name, cluster);
-}
-
 TEST(LibRadosAioEC, MultiWrite) {
   AioTestDataEC test_data;
   rados_completion_t my_completion, my_completion2, my_completion3;
@@ -3835,232 +1646,3 @@ TEST(LibRadosAioEC, MultiWrite) {
   rados_aio_release(my_completion2);
   rados_aio_release(my_completion3);
 }
-
-TEST(LibRadosAioEC, MultiWritePP) {
-  AioTestDataECPP test_data;
-  ASSERT_EQ("", test_data.init());
-  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  AioCompletion *my_completion_null = NULL;
-  ASSERT_NE(my_completion, my_completion_null);
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
-                                          bl1, sizeof(buf), 0));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion->get_return_value());
-
-  char buf2[64];
-  memset(buf2, 0xdd, sizeof(buf2));
-  bufferlist bl2;
-  bl2.append(buf2, sizeof(buf2));
-  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  ASSERT_NE(my_completion2, my_completion_null);
-  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion2,
-                                          bl2, sizeof(buf2), sizeof(buf)));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion2->wait_for_complete());
-  }
-  ASSERT_EQ(-EOPNOTSUPP, my_completion2->get_return_value());
-
-  bufferlist bl3;
-  AioCompletion *my_completion3 = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  ASSERT_NE(my_completion3, my_completion_null);
-  ASSERT_EQ(0, test_data.m_ioctx.aio_read("foo", my_completion3,
-                                         &bl3, (sizeof(buf) + sizeof(buf2) * 3), 0));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion3->wait_for_complete());
-  }
-  ASSERT_EQ((int)sizeof(buf), my_completion3->get_return_value());
-  ASSERT_EQ(sizeof(buf), bl3.length());
-  ASSERT_EQ(0, memcmp(bl3.c_str(), buf, sizeof(buf)));
-  delete my_completion;
-  delete my_completion2;
-  delete my_completion3;
-}
-
-TEST(LibRadosAio, RacingRemovePP) {
-  AioTestDataPP test_data;
-  ASSERT_EQ("", test_data.init({{"objecter_retry_writes_after_first_reply", "true"}}));
-  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
-        nullptr, nullptr, nullptr);
-  ASSERT_NE(my_completion, nullptr);
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl;
-  bl.append(buf, sizeof(buf));
-  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
-        nullptr, nullptr, nullptr);
-  ASSERT_NE(my_completion2, nullptr);
-  ASSERT_EQ(0, test_data.m_ioctx.aio_remove("foo", my_completion2));
-  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
-                                         bl, sizeof(buf), 0));
-  {
-    TestAlarm alarm;
-    my_completion2->wait_for_complete();
-    my_completion->wait_for_complete();
-  }
-  ASSERT_EQ(-ENOENT, my_completion2->get_return_value());
-  ASSERT_EQ(0, my_completion->get_return_value());
-  ASSERT_EQ(0, test_data.m_ioctx.stat("foo", nullptr, nullptr));
-  delete my_completion;
-  delete my_completion2;
-}
-
-TEST(LibRadosAio, RoundTripCmpExtPP) {
-  AioTestDataPP test_data;
-  ASSERT_EQ("", test_data.init());
-  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  AioCompletion *my_completion_null = NULL;
-  ASSERT_NE(my_completion, my_completion_null);
-  char full[128];
-  memset(full, 0xcc, sizeof(full));
-  bufferlist bl1;
-  bl1.append(full, sizeof(full));
-  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
-                                          bl1, sizeof(full), 0));
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion->get_return_value());
-
-  /* compare with match */
-  bufferlist cbl;
-  cbl.append(full, sizeof(full));
-  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  ASSERT_EQ(0, test_data.m_ioctx.aio_cmpext("foo", my_completion2, 0, cbl));
-
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion2->wait_for_complete());
-  }
-  ASSERT_EQ(0, my_completion2->get_return_value());
-
-  /* compare with mismatch */
-  memset(full, 0xdd, sizeof(full));
-  cbl.clear();
-  cbl.append(full, sizeof(full));
-  AioCompletion *my_completion3 = test_data.m_cluster.aio_create_completion(
-         nullptr, nullptr, nullptr);
-  ASSERT_EQ(0, test_data.m_ioctx.aio_cmpext("foo", my_completion3, 0, cbl));
-
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, my_completion3->wait_for_complete());
-  }
-  ASSERT_EQ(-MAX_ERRNO, my_completion3->get_return_value());
-
-  delete my_completion;
-  delete my_completion2;
-  delete my_completion3;
-}
-
-TEST(LibRadosAio, RoundTripCmpExtPP2)
-{
-  int ret;
-  char buf[128];
-  char miscmp_buf[128];
-  bufferlist cbl;
-  Rados cluster;
-  std::string pool_name = get_temp_pool_name();
-  ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
-  IoCtx ioctx;
-  cluster.ioctx_create(pool_name.c_str(), ioctx);
-
-  boost::scoped_ptr<AioCompletion>
-                       wr_cmpl(cluster.aio_create_completion(0, 0, 0));
-  ObjectWriteOperation wr_op;
-  memset(buf, 0xcc, sizeof(buf));
-  memset(miscmp_buf, 0xdd, sizeof(miscmp_buf));
-  bufferlist bl;
-  bl.append(buf, sizeof(buf));
-
-  wr_op.write_full(bl);
-  wr_op.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
-  ioctx.aio_operate("test_obj", wr_cmpl.get(), &wr_op);
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, wr_cmpl->wait_for_complete());
-  }
-  EXPECT_EQ(0, wr_cmpl->get_return_value());
-
-  /* cmpext as write op. first match then mismatch */
-  boost::scoped_ptr<AioCompletion>
-                       wr_cmpext_cmpl(cluster.aio_create_completion(0, 0, 0));
-  cbl.append(buf, sizeof(buf));
-  ret = 0;
-
-  wr_op.cmpext(0, cbl, &ret);
-  wr_op.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
-  ioctx.aio_operate("test_obj", wr_cmpext_cmpl.get(), &wr_op);
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, wr_cmpext_cmpl->wait_for_complete());
-  }
-  EXPECT_EQ(0, wr_cmpext_cmpl->get_return_value());
-  EXPECT_EQ(0, ret);
-
-  boost::scoped_ptr<AioCompletion>
-                       wr_cmpext_cmpl2(cluster.aio_create_completion(0, 0, 0));
-  cbl.clear();
-  cbl.append(miscmp_buf, sizeof(miscmp_buf));
-  ret = 0;
-
-  wr_op.cmpext(0, cbl, &ret);
-  wr_op.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
-  ioctx.aio_operate("test_obj", wr_cmpext_cmpl2.get(), &wr_op);
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, wr_cmpext_cmpl2->wait_for_complete());
-  }
-  EXPECT_EQ(-MAX_ERRNO, wr_cmpext_cmpl2->get_return_value());
-  EXPECT_EQ(-MAX_ERRNO, ret);
-
-  /* cmpext as read op */
-  boost::scoped_ptr<AioCompletion>
-                       rd_cmpext_cmpl(cluster.aio_create_completion(0, 0, 0));
-  ObjectReadOperation rd_op;
-  cbl.clear();
-  cbl.append(buf, sizeof(buf));
-  ret = 0;
-  rd_op.cmpext(0, cbl, &ret);
-  rd_op.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
-  ioctx.aio_operate("test_obj", rd_cmpext_cmpl.get(), &rd_op, 0);
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, rd_cmpext_cmpl->wait_for_complete());
-  }
-  EXPECT_EQ(0, rd_cmpext_cmpl->get_return_value());
-  EXPECT_EQ(0, ret);
-
-  boost::scoped_ptr<AioCompletion>
-                       rd_cmpext_cmpl2(cluster.aio_create_completion(0, 0, 0));
-  cbl.clear();
-  cbl.append(miscmp_buf, sizeof(miscmp_buf));
-  ret = 0;
-
-  rd_op.cmpext(0, cbl, &ret);
-  rd_op.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
-  ioctx.aio_operate("test_obj", rd_cmpext_cmpl2.get(), &rd_op, 0);
-  {
-    TestAlarm alarm;
-    ASSERT_EQ(0, rd_cmpext_cmpl2->wait_for_complete());
-  }
-  EXPECT_EQ(-MAX_ERRNO, rd_cmpext_cmpl2->get_return_value());
-  EXPECT_EQ(-MAX_ERRNO, ret);
-
-  ioctx.remove("test_obj");
-  destroy_one_pool_pp(pool_name, cluster);
-}
diff --git a/src/test/librados/aio_cxx.cc b/src/test/librados/aio_cxx.cc
new file mode 100644 (file)
index 0000000..b812930
--- /dev/null
@@ -0,0 +1,2443 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <sstream>
+#include <string>
+#include <utility>
+#include <boost/scoped_ptr.hpp>
+
+#include "gtest/gtest.h"
+
+#include "common/errno.h"
+#include "include/err.h"
+#include "include/rados/librados.hpp"
+#include "include/types.h"
+#include "include/stringify.h"
+#include "include/scope_guard.h"
+
+#include "test_cxx.h"
+
+using namespace librados;
+using std::pair;
+using std::ostringstream;
+
+class AioTestDataPP
+{
+public:
+  AioTestDataPP()
+    : m_init(false)
+  {
+  }
+
+  ~AioTestDataPP()
+  {
+    if (m_init) {
+      m_ioctx.close();
+      destroy_one_pool_pp(m_pool_name, m_cluster);
+    }
+  }
+
+  std::string init()
+  {
+      return init({});
+  }
+
+  std::string init(const std::map<std::string, std::string> &config)
+  {
+    int ret;
+
+    m_pool_name = get_temp_pool_name();
+    std::string err = create_one_pool_pp(m_pool_name, m_cluster, config);
+    if (!err.empty()) {
+      ostringstream oss;
+      oss << "create_one_pool(" << m_pool_name << ") failed: error " << err;
+      return oss.str();
+    }
+    ret = m_cluster.ioctx_create(m_pool_name.c_str(), m_ioctx);
+    if (ret) {
+      destroy_one_pool_pp(m_pool_name, m_cluster);
+      ostringstream oss;
+      oss << "rados_ioctx_create failed: error " << ret;
+      return oss.str();
+    }
+    m_init = true;
+    return "";
+  }
+
+  Rados m_cluster;
+  IoCtx m_ioctx;
+  std::string m_pool_name;
+  bool m_init;
+};
+
+TEST(LibRadosAio, TooBigPP) {
+  AioTestDataPP test_data;
+  ASSERT_EQ("", test_data.init());
+
+  bufferlist bl;
+  AioCompletion *aio_completion = test_data.m_cluster.aio_create_completion(nullptr, NULL, NULL);
+  ASSERT_EQ(-E2BIG, test_data.m_ioctx.aio_write("foo", aio_completion, bl, UINT_MAX, 0));
+  ASSERT_EQ(-E2BIG, test_data.m_ioctx.aio_append("foo", aio_completion, bl, UINT_MAX));
+  // ioctx.aio_write_full no way to overflow bl.length()
+  delete aio_completion;
+}
+
+TEST(LibRadosAio, PoolQuotaPP) {
+  AioTestDataPP test_data;
+  ASSERT_EQ("", test_data.init());
+  string p = get_temp_pool_name();
+  ASSERT_EQ(0, test_data.m_cluster.pool_create(p.c_str()));
+  IoCtx ioctx;
+  ASSERT_EQ(0, test_data.m_cluster.ioctx_create(p.c_str(), ioctx));
+  ioctx.application_enable("rados", true);
+
+  bufferlist inbl;
+  ASSERT_EQ(0, test_data.m_cluster.mon_command(
+      "{\"prefix\": \"osd pool set-quota\", \"pool\": \"" + p +
+      "\", \"field\": \"max_bytes\", \"val\": \"4096\"}",
+      inbl, NULL, NULL));
+
+  bufferlist bl;
+  bufferptr z(4096);
+  bl.append(z);
+  int n;
+  for (n = 0; n < 1024; ++n) {
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    librados::AioCompletion *completion =
+      test_data.m_cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate(
+       "foo" + stringify(n), completion, &op,
+       librados::OPERATION_FULL_TRY));
+    completion->wait_for_safe();
+    int r = completion->get_return_value();
+    completion->release();
+    if (r == -EDQUOT)
+      break;
+    ASSERT_EQ(0, r);
+    sleep(1);
+  }
+  ASSERT_LT(n, 1024);
+
+  // make sure we have latest map that marked the pool full
+  test_data.m_cluster.wait_for_latest_osdmap();
+
+  // make sure we block without FULL_TRY
+  {
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    librados::AioCompletion *completion =
+      test_data.m_cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate("bar", completion, &op, 0));
+    sleep(5);
+    ASSERT_FALSE(completion->is_safe());
+    completion->release();
+  }
+
+  ioctx.close();
+  ASSERT_EQ(0, test_data.m_cluster.pool_delete(p.c_str()));
+}
+
+TEST(LibRadosAio, SimpleWritePP) {
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  {
+  AioTestDataPP test_data;
+  ASSERT_EQ("", test_data.init());
+  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  AioCompletion *my_completion_null = NULL;
+  ASSERT_NE(my_completion, my_completion_null);
+  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo",
+                              my_completion, bl1, sizeof(buf), 0));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion->get_return_value());
+  delete my_completion;
+  }
+
+  {
+  AioTestDataPP test_data;
+  ASSERT_EQ("", test_data.init());
+  test_data.m_ioctx.set_namespace("nspace");
+  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo",
+                              my_completion, bl1, sizeof(buf), 0));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion->get_return_value());
+  delete my_completion;
+  }
+}
+
+TEST(LibRadosAio, WaitForSafePP) {
+  AioTestDataPP test_data;
+  ASSERT_EQ("", test_data.init());
+  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  AioCompletion *my_completion_null = NULL;
+  ASSERT_NE(my_completion, my_completion_null);
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo",
+                              my_completion, bl1, sizeof(buf), 0));
+  TestAlarm alarm;
+  ASSERT_EQ(0, my_completion->wait_for_safe());
+  ASSERT_EQ(0, my_completion->get_return_value());
+  delete my_completion;
+}
+
+TEST(LibRadosAio, RoundTripPP) {
+  AioTestDataPP test_data;
+  ASSERT_EQ("", test_data.init());
+  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  AioCompletion *my_completion_null = NULL;
+  ASSERT_NE(my_completion, my_completion_null);
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
+                                          bl1, sizeof(buf), 0));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion->get_return_value());
+  bufferlist bl2;
+  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  ASSERT_NE(my_completion2, my_completion_null);
+  ASSERT_EQ(0, test_data.m_ioctx.aio_read("foo",
+                             my_completion2, &bl2, sizeof(buf), 0));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion2->wait_for_complete());
+  }
+  ASSERT_EQ((int)sizeof(buf), my_completion2->get_return_value());
+  ASSERT_EQ(sizeof(buf), bl2.length());
+  ASSERT_EQ(0, memcmp(buf, bl2.c_str(), sizeof(buf)));
+  delete my_completion;
+  delete my_completion2;
+}
+
+TEST(LibRadosAio, RoundTripPP2) {
+  AioTestDataPP test_data;
+  ASSERT_EQ("", test_data.init());
+  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  AioCompletion *my_completion_null = NULL;
+  ASSERT_NE(my_completion, my_completion_null);
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
+                                          bl1, sizeof(buf), 0));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion->wait_for_complete());
+   }
+  ASSERT_EQ(0, my_completion->get_return_value());
+  bufferlist bl2;
+  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  ASSERT_NE(my_completion2, my_completion_null);
+  ASSERT_EQ(0, test_data.m_ioctx.aio_read("foo",
+                             my_completion2, &bl2, sizeof(buf), 0));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion2->wait_for_safe());
+    ASSERT_EQ(0, my_completion2->wait_for_complete());
+  }
+  ASSERT_EQ((int)sizeof(buf), my_completion2->get_return_value());
+  ASSERT_EQ(sizeof(buf), bl2.length());
+  ASSERT_EQ(0, memcmp(buf, bl2.c_str(), sizeof(buf)));
+  delete my_completion;
+  delete my_completion2;
+}
+
+//using ObjectWriteOperation/ObjectReadOperation with iohint
+TEST(LibRadosAio, RoundTripPP3)
+{
+  Rados cluster;
+  std::string pool_name = get_temp_pool_name();
+  ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+  IoCtx ioctx;
+  cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+  boost::scoped_ptr<AioCompletion> my_completion1(cluster.aio_create_completion(0, 0, 0));
+  ObjectWriteOperation op;
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl;
+  bl.append(buf, sizeof(buf));
+
+  op.write(0, bl);
+  op.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+  ioctx.aio_operate("test_obj", my_completion1.get(), &op);
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion1->wait_for_complete());
+  }
+  EXPECT_EQ(0, my_completion1->get_return_value());
+
+  boost::scoped_ptr<AioCompletion> my_completion2(cluster.aio_create_completion(0, 0, 0));
+  bl.clear();
+  ObjectReadOperation op1;
+  op1.read(0, sizeof(buf), &bl, NULL);
+  op1.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED|LIBRADOS_OP_FLAG_FADVISE_RANDOM);
+  bufferlist init_value_bl;
+  encode(static_cast<int32_t>(-1), init_value_bl);
+  bufferlist csum_bl;
+  op1.checksum(LIBRADOS_CHECKSUM_TYPE_CRC32C, init_value_bl,
+              0, 0, 0, &csum_bl, nullptr);
+  ioctx.aio_operate("test_obj", my_completion2.get(), &op1, 0);
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion2->wait_for_complete());
+  }
+  EXPECT_EQ(0, my_completion2->get_return_value());
+  ASSERT_EQ(0, memcmp(buf, bl.c_str(), sizeof(buf)));
+
+  ASSERT_EQ(8U, csum_bl.length());
+  auto csum_bl_it = csum_bl.cbegin();
+  uint32_t csum_count;
+  uint32_t csum;
+  decode(csum_count, csum_bl_it);
+  ASSERT_EQ(1U, csum_count);
+  decode(csum, csum_bl_it);
+  ASSERT_EQ(bl.crc32c(-1), csum);
+  ioctx.remove("test_obj");
+  destroy_one_pool_pp(pool_name, cluster);
+}
+
+TEST(LibRadosAio, RoundTripSparseReadPP) {
+  AioTestDataPP test_data;
+  ASSERT_EQ("", test_data.init());
+  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  AioCompletion *my_completion_null = NULL;
+  ASSERT_NE(my_completion, my_completion_null);
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
+                                          bl1, sizeof(buf), 0));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion->wait_for_complete());
+   }
+  ASSERT_EQ(0, my_completion->get_return_value());
+  std::map<uint64_t, uint64_t> extents;
+  bufferlist bl2;
+  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  ASSERT_NE(my_completion2, my_completion_null);
+  ASSERT_EQ(0, test_data.m_ioctx.aio_sparse_read("foo",
+                             my_completion2, &extents, &bl2, sizeof(buf), 0));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion2->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion2->get_return_value());
+  assert_eq_sparse(bl1, extents, bl2);
+  delete my_completion;
+  delete my_completion2;
+}
+
+TEST(LibRadosAioPP, XattrsRoundTripPP) {
+  char buf[128];
+  char attr1[] = "attr1";
+  char attr1_buf[] = "foo bar baz";
+  memset(buf, 0xaa, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  AioTestDataPP test_data;
+  ASSERT_EQ("", test_data.init());
+  ASSERT_EQ(0, test_data.m_ioctx.append("foo", bl1, sizeof(buf)));
+  bufferlist bl2;
+  // async getxattr
+  boost::scoped_ptr<AioCompletion> my_completion
+    (test_data.m_cluster.aio_create_completion
+     (nullptr, nullptr, nullptr));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_getxattr("foo", my_completion.get(), attr1, bl2));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion->wait_for_complete());
+  }
+  ASSERT_EQ(-ENODATA, my_completion->get_return_value());
+  // append
+  bufferlist bl3;
+  bl3.append(attr1_buf, sizeof(attr1_buf));
+  // async setxattr
+  AioTestDataPP test_data2;
+  ASSERT_EQ("", test_data2.init());
+  boost::scoped_ptr<AioCompletion> my_completion2
+    (test_data.m_cluster.aio_create_completion
+     (nullptr, nullptr, nullptr));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_setxattr("foo", my_completion2.get(), attr1, bl3));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion2->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion2->get_return_value());
+  // async getxattr
+  bufferlist bl4;
+  AioTestDataPP test_data3;
+  ASSERT_EQ("", test_data3.init());
+  boost::scoped_ptr<AioCompletion> my_completion3
+    (test_data.m_cluster.aio_create_completion
+     (nullptr, nullptr, nullptr));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_getxattr("foo", my_completion3.get(), attr1, bl4));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion3->wait_for_complete());
+  }
+  ASSERT_EQ((int)sizeof(attr1_buf), my_completion3->get_return_value());
+  // check content of attribute
+  ASSERT_EQ(0, memcmp(bl4.c_str(), attr1_buf, sizeof(attr1_buf)));
+}
+
+TEST(LibRadosAioPP, RmXattrPP) {
+  char buf[128];
+  char attr1[] = "attr1";
+  char attr1_buf[] = "foo bar baz";
+  memset(buf, 0xaa, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  AioTestDataPP test_data;
+  ASSERT_EQ("", test_data.init());
+  ASSERT_EQ(0, test_data.m_ioctx.append("foo", bl1, sizeof(buf)));
+  // async setxattr
+  bufferlist bl2;
+  bl2.append(attr1_buf, sizeof(attr1_buf));
+  boost::scoped_ptr<AioCompletion> my_completion
+    (test_data.m_cluster.aio_create_completion
+     (nullptr, nullptr, nullptr));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_setxattr("foo", my_completion.get(), attr1, bl2));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion->get_return_value());
+  // async rmxattr
+  AioTestDataPP test_data2;
+  ASSERT_EQ("", test_data2.init());
+  boost::scoped_ptr<AioCompletion> my_completion2
+    (test_data.m_cluster.aio_create_completion
+     (nullptr, nullptr, nullptr));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_rmxattr("foo", my_completion2.get(), attr1));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion2->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion2->get_return_value());
+  // async getxattr
+  AioTestDataPP test_data3;
+  ASSERT_EQ("", test_data3.init());
+  boost::scoped_ptr<AioCompletion> my_completion3
+    (test_data.m_cluster.aio_create_completion
+     (nullptr, nullptr, nullptr));
+  bufferlist bl3;
+  ASSERT_EQ(0, test_data.m_ioctx.aio_getxattr("foo", my_completion3.get(), attr1, bl3));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion3->wait_for_complete());
+  }
+  ASSERT_EQ(-ENODATA, my_completion3->get_return_value());
+  // Test rmxattr on a removed object
+  char buf2[128];
+  char attr2[] = "attr2";
+  char attr2_buf[] = "foo bar baz";
+  memset(buf2, 0xbb, sizeof(buf2));
+  bufferlist bl21;
+  bl21.append(buf, sizeof(buf));
+  ASSERT_EQ(0, test_data.m_ioctx.write("foo_rmxattr", bl21, sizeof(buf2), 0));
+  bufferlist bl22;
+  bl22.append(attr2_buf, sizeof(attr2_buf));
+  // async setxattr
+  AioTestDataPP test_data4;
+  ASSERT_EQ("", test_data4.init());
+  boost::scoped_ptr<AioCompletion> my_completion4
+    (test_data.m_cluster.aio_create_completion
+     (nullptr, nullptr, nullptr));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_setxattr("foo_rmxattr", my_completion4.get(), attr2, bl22));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion4->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion4->get_return_value());
+  // remove object
+  ASSERT_EQ(0, test_data.m_ioctx.remove("foo_rmxattr"));
+  // async rmxattr on non existing object
+  AioTestDataPP test_data5;
+  ASSERT_EQ("", test_data5.init());
+  boost::scoped_ptr<AioCompletion> my_completion5
+    (test_data.m_cluster.aio_create_completion
+     (nullptr, nullptr, nullptr));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_rmxattr("foo_rmxattr", my_completion5.get(), attr2));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion5->wait_for_complete());
+  }
+  ASSERT_EQ(-ENOENT, my_completion5->get_return_value());
+}
+
+TEST(LibRadosIoPP, XattrListPP) {
+  AioTestDataPP test_data;
+  ASSERT_EQ("", test_data.init());
+  // create an object with 2 attributes
+  char buf[128];
+  char attr1[] = "attr1";
+  char attr1_buf[] = "foo bar baz";
+  char attr2[] = "attr2";
+  char attr2_buf[256];
+  for (size_t j = 0; j < sizeof(attr2_buf); ++j) {
+    attr2_buf[j] = j % 0xff;
+  }
+  memset(buf, 0xaa, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, test_data.m_ioctx.append("foo", bl1, sizeof(buf)));
+  bufferlist bl2;
+  bl2.append(attr1_buf, sizeof(attr1_buf));
+  ASSERT_EQ(0, test_data.m_ioctx.setxattr("foo", attr1, bl2));
+  bufferlist bl3;
+  bl3.append(attr2_buf, sizeof(attr2_buf));
+  ASSERT_EQ(0, test_data.m_ioctx.setxattr("foo", attr2, bl3));
+  // call async version of getxattrs
+  boost::scoped_ptr<AioCompletion> my_completion
+    (test_data.m_cluster.aio_create_completion
+     (nullptr, nullptr, nullptr));
+  std::map<std::string, bufferlist> attrset;
+  ASSERT_EQ(0, test_data.m_ioctx.aio_getxattrs("foo", my_completion.get(), attrset));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion->get_return_value());
+  for (std::map<std::string, bufferlist>::iterator i = attrset.begin();
+       i != attrset.end(); ++i) {
+    if (i->first == string(attr1)) {
+      ASSERT_EQ(0, memcmp(i->second.c_str(), attr1_buf, sizeof(attr1_buf)));
+    }
+    else if (i->first == string(attr2)) {
+      ASSERT_EQ(0, memcmp(i->second.c_str(), attr2_buf, sizeof(attr2_buf)));
+    }
+    else {
+      ASSERT_EQ(0, 1);
+    }
+  }
+}
+
+TEST(LibRadosAio, IsCompletePP) {
+  AioTestDataPP test_data;
+  ASSERT_EQ("", test_data.init());
+  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  AioCompletion *my_completion_null = NULL;
+  ASSERT_NE(my_completion, my_completion_null);
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
+                                          bl1, sizeof(buf), 0));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion->get_return_value());
+  bufferlist bl2;
+  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  ASSERT_NE(my_completion2, my_completion_null);
+  ASSERT_EQ(0, test_data.m_ioctx.aio_read("foo", my_completion2,
+                                         &bl2, sizeof(buf), 0));
+  {
+    TestAlarm alarm;
+
+    // Busy-wait until the AIO completes.
+    // Normally we wouldn't do this, but we want to test is_complete.
+    while (true) {
+      int is_complete = my_completion2->is_complete();
+      if (is_complete)
+       break;
+    }
+  }
+  ASSERT_EQ((int)sizeof(buf), my_completion2->get_return_value());
+  ASSERT_EQ(sizeof(buf), bl2.length());
+  ASSERT_EQ(0, memcmp(buf, bl2.c_str(), sizeof(buf)));
+  delete my_completion;
+  delete my_completion2;
+}
+
+TEST(LibRadosAio, IsSafePP) {
+  AioTestDataPP test_data;
+  ASSERT_EQ("", test_data.init());
+  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  AioCompletion *my_completion_null = NULL;
+  ASSERT_NE(my_completion, my_completion_null);
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
+                                          bl1, sizeof(buf), 0));
+  {
+    TestAlarm alarm;
+
+    // Busy-wait until the AIO completes.
+    // Normally we wouldn't do this, but we want to test rados_aio_is_safe.
+    while (true) {
+      int is_safe = my_completion->is_safe();
+      if (is_safe)
+       break;
+    }
+  }
+  ASSERT_EQ(0, my_completion->get_return_value());
+  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  bufferlist bl2;
+  ASSERT_NE(my_completion2, my_completion_null);
+  ASSERT_EQ(0, test_data.m_ioctx.aio_read("foo", my_completion2,
+                                         &bl2, sizeof(buf), 0));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion2->wait_for_complete());
+  }
+  ASSERT_EQ((int)sizeof(buf), my_completion2->get_return_value());
+  ASSERT_EQ(sizeof(buf), bl2.length());
+  ASSERT_EQ(0, memcmp(buf, bl2.c_str(), sizeof(buf)));
+  delete my_completion;
+  delete my_completion2;
+}
+
+TEST(LibRadosAio, ReturnValuePP) {
+  AioTestDataPP test_data;
+  ASSERT_EQ("", test_data.init());
+  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  AioCompletion *my_completion_null = NULL;
+  ASSERT_NE(my_completion, my_completion_null);
+  bufferlist bl1;
+  ASSERT_EQ(0, test_data.m_ioctx.aio_read("nonexistent",
+                              my_completion, &bl1, 128, 0));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion->wait_for_complete());
+  }
+  ASSERT_EQ(-ENOENT, my_completion->get_return_value());
+  delete my_completion;
+}
+
+TEST(LibRadosAio, FlushPP) {
+  AioTestDataPP test_data;
+  ASSERT_EQ("", test_data.init());
+  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  AioCompletion *my_completion_null = NULL;
+  ASSERT_NE(my_completion, my_completion_null);
+  char buf[128];
+  memset(buf, 0xee, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
+                                          bl1, sizeof(buf), 0));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_flush());
+  ASSERT_EQ(0, my_completion->get_return_value());
+  bufferlist bl2;
+  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  ASSERT_NE(my_completion2, my_completion_null);
+  ASSERT_EQ(0, test_data.m_ioctx.aio_read("foo", my_completion2,
+                                         &bl2, sizeof(buf), 0));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion2->wait_for_complete());
+  }
+  ASSERT_EQ((int)sizeof(buf), my_completion2->get_return_value());
+  ASSERT_EQ(sizeof(buf), bl2.length());
+  ASSERT_EQ(0, memcmp(buf, bl2.c_str(), sizeof(buf)));
+  delete my_completion;
+  delete my_completion2;
+}
+
+TEST(LibRadosAio, FlushAsyncPP) {
+  AioTestDataPP test_data;
+  ASSERT_EQ("", test_data.init());
+  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  AioCompletion *flush_completion =
+      test_data.m_cluster.aio_create_completion(NULL, NULL, NULL);
+  AioCompletion *my_completion_null = NULL;
+  ASSERT_NE(my_completion, my_completion_null);
+  char buf[128];
+  memset(buf, 0xee, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
+                                          bl1, sizeof(buf), 0));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_flush_async(flush_completion));
+  {
+      TestAlarm alarm;
+      ASSERT_EQ(0, flush_completion->wait_for_complete());
+      ASSERT_EQ(0, flush_completion->wait_for_safe());
+  }
+  ASSERT_EQ(1, my_completion->is_complete());
+  ASSERT_EQ(1, my_completion->is_safe());
+  ASSERT_EQ(1, flush_completion->is_complete());
+  ASSERT_EQ(1, flush_completion->is_safe());
+  ASSERT_EQ(0, my_completion->get_return_value());
+  bufferlist bl2;
+  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  ASSERT_NE(my_completion2, my_completion_null);
+  ASSERT_EQ(0, test_data.m_ioctx.aio_read("foo", my_completion2,
+                                         &bl2, sizeof(buf), 0));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion2->wait_for_complete());
+  }
+  ASSERT_EQ((int)sizeof(buf), my_completion2->get_return_value());
+  ASSERT_EQ(sizeof(buf), bl2.length());
+  ASSERT_EQ(0, memcmp(buf, bl2.c_str(), sizeof(buf)));
+  delete my_completion;
+  delete my_completion2;
+  delete flush_completion;
+}
+
+TEST(LibRadosAio, RoundTripWriteFullPP) {
+  AioTestDataPP test_data;
+  ASSERT_EQ("", test_data.init());
+  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  AioCompletion *my_completion_null = NULL;
+  ASSERT_NE(my_completion, my_completion_null);
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
+                                          bl1, sizeof(buf), 0));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion->get_return_value());
+  char buf2[64];
+  memset(buf2, 0xdd, sizeof(buf2));
+  bufferlist bl2;
+  bl2.append(buf2, sizeof(buf2));
+  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  ASSERT_NE(my_completion2, my_completion_null);
+  ASSERT_EQ(0, test_data.m_ioctx.aio_write_full("foo", my_completion2, bl2));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion2->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion2->get_return_value());
+  bufferlist bl3;
+  AioCompletion *my_completion3 = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  ASSERT_NE(my_completion3, my_completion_null);
+  ASSERT_EQ(0, test_data.m_ioctx.aio_read("foo", my_completion3,
+                                         &bl3, sizeof(buf), 0));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion3->wait_for_complete());
+  }
+  ASSERT_EQ((int)sizeof(buf2), my_completion3->get_return_value());
+  ASSERT_EQ(sizeof(buf2), bl3.length());
+  ASSERT_EQ(0, memcmp(bl3.c_str(), buf2, sizeof(buf2)));
+  delete my_completion;
+  delete my_completion2;
+  delete my_completion3;
+}
+
+//using ObjectWriteOperation/ObjectReadOperation with iohint
+TEST(LibRadosAio, RoundTripWriteFullPP2)
+{
+  Rados cluster;
+  std::string pool_name = get_temp_pool_name();
+  ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+  IoCtx ioctx;
+  cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+  boost::scoped_ptr<AioCompletion> my_completion1(cluster.aio_create_completion(0, 0, 0));
+  ObjectWriteOperation op;
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl;
+  bl.append(buf);
+
+  op.write_full(bl);
+  op.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+  ioctx.aio_operate("test_obj", my_completion1.get(), &op);
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion1->wait_for_complete());
+  }
+  EXPECT_EQ(0, my_completion1->get_return_value());
+
+  boost::scoped_ptr<AioCompletion> my_completion2(cluster.aio_create_completion(0, 0, 0));
+  bl.clear();
+  ObjectReadOperation op1;
+  op1.read(0, sizeof(buf), &bl, NULL);
+  op1.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED|LIBRADOS_OP_FLAG_FADVISE_RANDOM);
+  ioctx.aio_operate("test_obj", my_completion2.get(), &op1, 0);
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion2->wait_for_complete());
+  }
+  EXPECT_EQ(0, my_completion2->get_return_value());
+  ASSERT_EQ(0, memcmp(buf, bl.c_str(), sizeof(buf)));
+
+  ioctx.remove("test_obj");
+  destroy_one_pool_pp(pool_name, cluster);
+}
+
+TEST(LibRadosAio, RoundTripWriteSamePP) {
+  AioTestDataPP test_data;
+  ASSERT_EQ("", test_data.init());
+  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  AioCompletion *my_completion_null = NULL;
+  ASSERT_NE(my_completion, my_completion_null);
+  char full[128];
+  memset(full, 0xcc, sizeof(full));
+  bufferlist bl1;
+  bl1.append(full, sizeof(full));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
+                                          bl1, sizeof(full), 0));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion->get_return_value());
+  /* write the same buf four times */
+  char buf[32];
+  size_t ws_write_len = sizeof(full);
+  memset(buf, 0xdd, sizeof(buf));
+  bufferlist bl2;
+  bl2.append(buf, sizeof(buf));
+  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  ASSERT_NE(my_completion2, my_completion_null);
+  ASSERT_EQ(0, test_data.m_ioctx.aio_writesame("foo", my_completion2, bl2,
+                                              ws_write_len, 0));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion2->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion2->get_return_value());
+  bufferlist bl3;
+  AioCompletion *my_completion3 = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  ASSERT_NE(my_completion3, my_completion_null);
+  ASSERT_EQ(0, test_data.m_ioctx.aio_read("foo", my_completion3,
+                                         &bl3, sizeof(full), 0));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion3->wait_for_complete());
+  }
+  ASSERT_EQ((int)sizeof(full), my_completion3->get_return_value());
+  ASSERT_EQ(sizeof(full), bl3.length());
+  for (char *cmp = bl3.c_str(); cmp < bl3.c_str() + bl3.length();
+                                                       cmp += sizeof(buf)) {
+    ASSERT_EQ(0, memcmp(cmp, buf, sizeof(buf)));
+  }
+  delete my_completion;
+  delete my_completion2;
+  delete my_completion3;
+}
+
+TEST(LibRadosAio, RoundTripWriteSamePP2)
+{
+  Rados cluster;
+  std::string pool_name = get_temp_pool_name();
+  ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+  IoCtx ioctx;
+  cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+  boost::scoped_ptr<AioCompletion>
+                       wr_cmpl(cluster.aio_create_completion(0, 0, 0));
+  ObjectWriteOperation op;
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl;
+  bl.append(buf, sizeof(buf));
+
+  op.writesame(0, sizeof(buf) * 4, bl);
+  op.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+  ioctx.aio_operate("test_obj", wr_cmpl.get(), &op);
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, wr_cmpl->wait_for_complete());
+  }
+  EXPECT_EQ(0, wr_cmpl->get_return_value());
+
+  boost::scoped_ptr<AioCompletion>
+                       rd_cmpl(cluster.aio_create_completion(0, 0, 0));
+  char *cmp;
+  char full[sizeof(buf) * 4];
+  memset(full, 0, sizeof(full));
+  bufferlist fl;
+  fl.append(full, sizeof(full));
+  ObjectReadOperation op1;
+  op1.read(0, sizeof(full), &fl, NULL);
+  op1.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+  ioctx.aio_operate("test_obj", rd_cmpl.get(), &op1, 0);
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, rd_cmpl->wait_for_complete());
+  }
+  EXPECT_EQ(0, rd_cmpl->get_return_value());
+  for (cmp = fl.c_str(); cmp < fl.c_str() + fl.length(); cmp += sizeof(buf)) {
+    ASSERT_EQ(0, memcmp(cmp, buf, sizeof(buf)));
+  }
+
+  ioctx.remove("test_obj");
+  destroy_one_pool_pp(pool_name, cluster);
+}
+
+TEST(LibRadosAio, SimpleStatPPNS) {
+  AioTestDataPP test_data;
+  ASSERT_EQ("", test_data.init());
+  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  AioCompletion *my_completion_null = NULL;
+  ASSERT_NE(my_completion, my_completion_null);
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
+                                          bl1, sizeof(buf), 0));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion->get_return_value());
+  uint64_t psize;
+  time_t pmtime;
+  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  ASSERT_NE(my_completion2, my_completion_null);
+  ASSERT_EQ(0, test_data.m_ioctx.aio_stat("foo", my_completion2,
+                                       &psize, &pmtime));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion2->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion2->get_return_value());
+  ASSERT_EQ(sizeof(buf), psize);
+  delete my_completion;
+  delete my_completion2;
+}
+
+TEST(LibRadosAio, SimpleStatPP) {
+  AioTestDataPP test_data;
+  ASSERT_EQ("", test_data.init());
+  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  AioCompletion *my_completion_null = NULL;
+  ASSERT_NE(my_completion, my_completion_null);
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
+                                          bl1, sizeof(buf), 0));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion->get_return_value());
+  uint64_t psize;
+  time_t pmtime;
+  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  ASSERT_NE(my_completion2, my_completion_null);
+  ASSERT_EQ(0, test_data.m_ioctx.aio_stat("foo", my_completion2,
+                                       &psize, &pmtime));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion2->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion2->get_return_value());
+  ASSERT_EQ(sizeof(buf), psize);
+  delete my_completion;
+  delete my_completion2;
+}
+
+TEST(LibRadosAio, StatRemovePP) {
+  AioTestDataPP test_data;
+  ASSERT_EQ("", test_data.init());
+  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  AioCompletion *my_completion_null = NULL;
+  ASSERT_NE(my_completion, my_completion_null);
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
+                                          bl1, sizeof(buf), 0));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion->get_return_value());
+  uint64_t psize;
+  time_t pmtime;
+  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  ASSERT_NE(my_completion2, my_completion_null);
+  ASSERT_EQ(0, test_data.m_ioctx.aio_stat("foo", my_completion2,
+                                       &psize, &pmtime));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion2->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion2->get_return_value());
+  ASSERT_EQ(sizeof(buf), psize);
+  uint64_t psize2;
+  time_t pmtime2;
+  AioCompletion *my_completion3 = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  ASSERT_NE(my_completion3, my_completion_null);
+  ASSERT_EQ(0, test_data.m_ioctx.aio_remove("foo", my_completion3));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion3->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion3->get_return_value());
+
+  AioCompletion *my_completion4 = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  ASSERT_NE(my_completion4, my_completion_null);
+  ASSERT_EQ(0, test_data.m_ioctx.aio_stat("foo", my_completion4,
+                                       &psize2, &pmtime2));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion4->wait_for_complete());
+  }
+  ASSERT_EQ(-ENOENT, my_completion4->get_return_value());
+  delete my_completion;
+  delete my_completion2;
+  delete my_completion3;
+  delete my_completion4;
+}
+
+TEST(LibRadosAio, ExecuteClassPP) {
+  AioTestDataPP test_data;
+  ASSERT_EQ("", test_data.init());
+  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  AioCompletion *my_completion_null = NULL;
+  ASSERT_NE(my_completion, my_completion_null);
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
+                                          bl1, sizeof(buf), 0));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion->get_return_value());
+  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  ASSERT_NE(my_completion2, my_completion_null);
+  bufferlist in, out;
+  ASSERT_EQ(0, test_data.m_ioctx.aio_exec("foo", my_completion2,
+                                         "hello", "say_hello", in, &out));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion2->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion2->get_return_value());
+  ASSERT_EQ(std::string("Hello, world!"), std::string(out.c_str(), out.length()));
+  delete my_completion;
+  delete my_completion2;
+}
+
+using std::string;
+using std::map;
+using std::set;
+
+TEST(LibRadosAio, OmapPP) {
+  Rados cluster;
+  std::string pool_name = get_temp_pool_name();
+  ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+  IoCtx ioctx;
+  cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+  string header_str = "baz";
+  bufferptr bp(header_str.c_str(), header_str.size() + 1);
+  bufferlist header_to_set;
+  header_to_set.push_back(bp);
+  map<string, bufferlist> to_set;
+  {
+    boost::scoped_ptr<AioCompletion> my_completion(cluster.aio_create_completion(0, 0, 0));
+    ObjectWriteOperation op;
+    to_set["foo"] = header_to_set;
+    to_set["foo2"] = header_to_set;
+    to_set["qfoo3"] = header_to_set;
+    op.omap_set(to_set);
+
+    op.omap_set_header(header_to_set);
+
+    ioctx.aio_operate("test_obj", my_completion.get(), &op);
+    {
+      TestAlarm alarm;
+      ASSERT_EQ(0, my_completion->wait_for_complete());
+    }
+    EXPECT_EQ(0, my_completion->get_return_value());
+  }
+
+  {
+    boost::scoped_ptr<AioCompletion> my_completion(cluster.aio_create_completion(0, 0, 0));
+    ObjectReadOperation op;
+    map<string, pair<bufferlist, int> > assertions;
+    bufferlist val;
+    val.append(string("bar"));
+    assertions["foo"] = pair<bufferlist, int>(val, CEPH_OSD_CMPXATTR_OP_EQ);
+
+    int r;
+    op.omap_cmp(assertions, &r);
+
+    ioctx.aio_operate("test_obj", my_completion.get(), &op, 0);
+    {
+      TestAlarm alarm;
+      ASSERT_EQ(0, my_completion->wait_for_complete());
+    }
+    EXPECT_EQ(-ECANCELED, my_completion->get_return_value());
+    ASSERT_EQ(-ECANCELED, r);
+  }
+
+  {
+    boost::scoped_ptr<AioCompletion> my_completion(cluster.aio_create_completion(0, 0, 0));
+    ObjectReadOperation op;
+
+    set<string> set_got;
+    map<string, bufferlist> map_got;
+
+    set<string> to_get;
+    map<string, bufferlist> got3;
+
+    map<string, bufferlist> got4;
+
+    bufferlist header;
+
+    op.omap_get_keys2("", 1, &set_got, nullptr, 0);
+    op.omap_get_vals2("foo", 1, &map_got, nullptr, 0);
+
+    to_get.insert("foo");
+    to_get.insert("qfoo3");
+    op.omap_get_vals_by_keys(to_get, &got3, 0);
+
+    op.omap_get_header(&header, 0);
+
+    op.omap_get_vals2("foo2", "q", 1, &got4, nullptr, 0);
+
+    ioctx.aio_operate("test_obj", my_completion.get(), &op, 0);
+    {
+      TestAlarm alarm;
+      ASSERT_EQ(0, my_completion->wait_for_complete());
+    }
+    EXPECT_EQ(0, my_completion->get_return_value());
+
+    ASSERT_EQ(header.length(), header_to_set.length());
+    ASSERT_EQ(set_got.size(), (unsigned)1);
+    ASSERT_EQ(*set_got.begin(), "foo");
+    ASSERT_EQ(map_got.size(), (unsigned)1);
+    ASSERT_EQ(map_got.begin()->first, "foo2");
+    ASSERT_EQ(got3.size(), (unsigned)2);
+    ASSERT_EQ(got3.begin()->first, "foo");
+    ASSERT_EQ(got3.rbegin()->first, "qfoo3");
+    ASSERT_EQ(got4.size(), (unsigned)1);
+    ASSERT_EQ(got4.begin()->first, "qfoo3");
+  }
+
+  {
+    boost::scoped_ptr<AioCompletion> my_completion(cluster.aio_create_completion(0, 0, 0));
+    ObjectWriteOperation op;
+    set<string> to_remove;
+    to_remove.insert("foo2");
+    op.omap_rm_keys(to_remove);
+    ioctx.aio_operate("test_obj", my_completion.get(), &op);
+    {
+      TestAlarm alarm;
+      ASSERT_EQ(0, my_completion->wait_for_complete());
+    }
+    EXPECT_EQ(0, my_completion->get_return_value());
+  }
+
+  {
+    boost::scoped_ptr<AioCompletion> my_completion(cluster.aio_create_completion(0, 0, 0));
+    ObjectReadOperation op;
+
+    set<string> set_got;
+    op.omap_get_keys2("", -1, &set_got, nullptr, 0);
+    ioctx.aio_operate("test_obj", my_completion.get(), &op, 0);
+    {
+      TestAlarm alarm;
+      ASSERT_EQ(0, my_completion->wait_for_complete());
+    }
+    EXPECT_EQ(0, my_completion->get_return_value());
+    ASSERT_EQ(set_got.size(), (unsigned)2);
+  }
+
+  {
+    boost::scoped_ptr<AioCompletion> my_completion(cluster.aio_create_completion(0, 0, 0));
+    ObjectWriteOperation op;
+    op.omap_clear();
+    ioctx.aio_operate("test_obj", my_completion.get(), &op);
+    {
+      TestAlarm alarm;
+      ASSERT_EQ(0, my_completion->wait_for_complete());
+    }
+    EXPECT_EQ(0, my_completion->get_return_value());
+  }
+
+  {
+    boost::scoped_ptr<AioCompletion> my_completion(cluster.aio_create_completion(0, 0, 0));
+    ObjectReadOperation op;
+
+    set<string> set_got;
+    op.omap_get_keys2("", -1, &set_got, nullptr, 0);
+    ioctx.aio_operate("test_obj", my_completion.get(), &op, 0);
+    {
+      TestAlarm alarm;
+      ASSERT_EQ(0, my_completion->wait_for_complete());
+    }
+    EXPECT_EQ(0, my_completion->get_return_value());
+    ASSERT_EQ(set_got.size(), (unsigned)0);
+  }
+
+  // omap_clear clears header *and* keys
+  {
+    boost::scoped_ptr<AioCompletion> my_completion(cluster.aio_create_completion(0, 0, 0));
+    ObjectWriteOperation op;
+    bufferlist bl;
+    bl.append("some data");
+    map<string,bufferlist> to_set;
+    to_set["foo"] = bl;
+    to_set["foo2"] = bl;
+    to_set["qfoo3"] = bl;
+    op.omap_set(to_set);
+    op.omap_set_header(bl);
+    ioctx.aio_operate("foo3", my_completion.get(), &op);
+    {
+      TestAlarm alarm;
+      ASSERT_EQ(0, my_completion->wait_for_complete());
+    }
+    EXPECT_EQ(0, my_completion->get_return_value());
+  }
+  {
+    boost::scoped_ptr<AioCompletion> my_completion(cluster.aio_create_completion(0, 0, 0));
+    ObjectWriteOperation op;
+    op.omap_clear();
+    ioctx.aio_operate("foo3", my_completion.get(), &op);
+    {
+      TestAlarm alarm;
+      ASSERT_EQ(0, my_completion->wait_for_complete());
+    }
+    EXPECT_EQ(0, my_completion->get_return_value());
+  }
+  {
+    boost::scoped_ptr<AioCompletion> my_completion(cluster.aio_create_completion(0, 0, 0));
+    ObjectReadOperation op;
+    set<string> set_got;
+    bufferlist hdr;
+    op.omap_get_keys2("", -1, &set_got, nullptr, 0);
+    op.omap_get_header(&hdr, NULL);
+    ioctx.aio_operate("foo3", my_completion.get(), &op, 0);
+    {
+      TestAlarm alarm;
+      ASSERT_EQ(0, my_completion->wait_for_complete());
+    }
+    EXPECT_EQ(0, my_completion->get_return_value());
+    ASSERT_EQ(set_got.size(), (unsigned)0);
+    ASSERT_EQ(hdr.length(), 0u);
+  }
+
+  ioctx.remove("test_obj");
+  destroy_one_pool_pp(pool_name, cluster);
+}
+
+TEST(LibRadosAio, MultiWritePP) {
+  AioTestDataPP test_data;
+  ASSERT_EQ("", test_data.init());
+  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  AioCompletion *my_completion_null = NULL;
+  ASSERT_NE(my_completion, my_completion_null);
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
+                                          bl1, sizeof(buf), 0));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion->get_return_value());
+
+  char buf2[64];
+  memset(buf2, 0xdd, sizeof(buf2));
+  bufferlist bl2;
+  bl2.append(buf2, sizeof(buf2));
+  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  ASSERT_NE(my_completion2, my_completion_null);
+  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion2,
+                                          bl2, sizeof(buf2), sizeof(buf)));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion2->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion2->get_return_value());
+
+  bufferlist bl3;
+  AioCompletion *my_completion3 = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  ASSERT_NE(my_completion3, my_completion_null);
+  ASSERT_EQ(0, test_data.m_ioctx.aio_read("foo", my_completion3,
+                                         &bl3, (sizeof(buf) + sizeof(buf2) * 3), 0));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion3->wait_for_complete());
+  }
+  ASSERT_EQ((int)(sizeof(buf) + sizeof(buf2)), my_completion3->get_return_value());
+  ASSERT_EQ(sizeof(buf) + sizeof(buf2), bl3.length());
+  ASSERT_EQ(0, memcmp(bl3.c_str(), buf, sizeof(buf)));
+  ASSERT_EQ(0, memcmp(bl3.c_str() + sizeof(buf), buf2, sizeof(buf2)));
+  delete my_completion;
+  delete my_completion2;
+  delete my_completion3;
+}
+
+TEST(LibRadosAio, AioUnlockPP) {
+  AioTestDataPP test_data;
+  ASSERT_EQ("", test_data.init());
+  ASSERT_EQ(0, test_data.m_ioctx.lock_exclusive("foo", "TestLock", "Cookie", "", NULL, 0));
+  boost::scoped_ptr<AioCompletion> my_completion
+    (test_data.m_cluster.aio_create_completion
+     (nullptr, nullptr, nullptr));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_unlock("foo", "TestLock", "Cookie", my_completion.get()));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion->get_return_value());
+  bufferlist bl2;
+  ASSERT_EQ(0, test_data.m_ioctx.lock_exclusive("foo", "TestLock", "Cookie", "", NULL, 0));
+}
+
+class AioTestDataECPP
+{
+public:
+  AioTestDataECPP()
+    : m_init(false)
+  {}
+
+  ~AioTestDataECPP()
+  {
+    if (m_init) {
+      m_ioctx.close();
+      destroy_one_ec_pool_pp(m_pool_name, m_cluster);
+    }
+  }
+
+  std::string init()
+  {
+    int ret;
+    m_pool_name = get_temp_pool_name();
+    std::string err = create_one_ec_pool_pp(m_pool_name, m_cluster);
+    if (!err.empty()) {
+      ostringstream oss;
+      oss << "create_one_ec_pool(" << m_pool_name << ") failed: error " << err;
+      return oss.str();
+    }
+    ret = m_cluster.ioctx_create(m_pool_name.c_str(), m_ioctx);
+    if (ret) {
+      destroy_one_ec_pool_pp(m_pool_name, m_cluster);
+      ostringstream oss;
+      oss << "rados_ioctx_create failed: error " << ret;
+      return oss.str();
+    }
+    m_init = true;
+    return "";
+  }
+
+  Rados m_cluster;
+  IoCtx m_ioctx;
+  std::string m_pool_name;
+  bool m_init;
+};
+
+// EC test cases
+TEST(LibRadosAioEC, SimpleWritePP) {
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  {
+  AioTestDataECPP test_data;
+  ASSERT_EQ("", test_data.init());
+  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  AioCompletion *my_completion_null = NULL;
+  ASSERT_NE(my_completion, my_completion_null);
+  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo",
+                              my_completion, bl1, sizeof(buf), 0));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion->get_return_value());
+  delete my_completion;
+  }
+
+  {
+  AioTestDataECPP test_data;
+  ASSERT_EQ("", test_data.init());
+  test_data.m_ioctx.set_namespace("nspace");
+  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo",
+                              my_completion, bl1, sizeof(buf), 0));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion->get_return_value());
+  delete my_completion;
+  }
+}
+
+TEST(LibRadosAioEC, WaitForSafePP) {
+  AioTestDataECPP test_data;
+  ASSERT_EQ("", test_data.init());
+  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  AioCompletion *my_completion_null = NULL;
+  ASSERT_NE(my_completion, my_completion_null);
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo",
+                              my_completion, bl1, sizeof(buf), 0));
+  TestAlarm alarm;
+  ASSERT_EQ(0, my_completion->wait_for_safe());
+  ASSERT_EQ(0, my_completion->get_return_value());
+  delete my_completion;
+}
+
+TEST(LibRadosAioEC, RoundTripPP) {
+  AioTestDataECPP test_data;
+  ASSERT_EQ("", test_data.init());
+  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  AioCompletion *my_completion_null = NULL;
+  ASSERT_NE(my_completion, my_completion_null);
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
+                                          bl1, sizeof(buf), 0));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion->get_return_value());
+  bufferlist bl2;
+  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  ASSERT_NE(my_completion2, my_completion_null);
+  ASSERT_EQ(0, test_data.m_ioctx.aio_read("foo",
+                             my_completion2, &bl2, sizeof(buf), 0));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion2->wait_for_complete());
+  }
+  ASSERT_EQ((int)sizeof(buf), my_completion2->get_return_value());
+  ASSERT_EQ(sizeof(buf), bl2.length());
+  ASSERT_EQ(0, memcmp(buf, bl2.c_str(), sizeof(buf)));
+  delete my_completion;
+  delete my_completion2;
+}
+
+TEST(LibRadosAioEC, RoundTripPP2) {
+  AioTestDataECPP test_data;
+  ASSERT_EQ("", test_data.init());
+  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  AioCompletion *my_completion_null = NULL;
+  ASSERT_NE(my_completion, my_completion_null);
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
+                                          bl1, sizeof(buf), 0));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion->get_return_value());
+  bufferlist bl2;
+  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  ASSERT_NE(my_completion2, my_completion_null);
+  ASSERT_EQ(0, test_data.m_ioctx.aio_read("foo",
+                             my_completion2, &bl2, sizeof(buf), 0));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion2->wait_for_safe());
+    ASSERT_EQ(0, my_completion2->wait_for_complete());
+  }
+  ASSERT_EQ((int)sizeof(buf), my_completion2->get_return_value());
+  ASSERT_EQ(sizeof(buf), bl2.length());
+  ASSERT_EQ(0, memcmp(buf, bl2.c_str(), sizeof(buf)));
+  delete my_completion;
+  delete my_completion2;
+}
+
+//using ObjectWriteOperation/ObjectReadOperation with iohint
+TEST(LibRadosAioEC, RoundTripPP3)
+{
+  Rados cluster;
+  std::string pool_name = get_temp_pool_name();
+  ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+  IoCtx ioctx;
+  cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+  boost::scoped_ptr<AioCompletion> my_completion1(cluster.aio_create_completion(0, 0, 0));
+  ObjectWriteOperation op;
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl;
+  bl.append(buf);
+
+  op.write(0, bl);
+  op.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+  ioctx.aio_operate("test_obj", my_completion1.get(), &op);
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion1->wait_for_complete());
+  }
+  EXPECT_EQ(0, my_completion1->get_return_value());
+
+  boost::scoped_ptr<AioCompletion> my_completion2(cluster.aio_create_completion(0, 0, 0));
+  bl.clear();
+  ObjectReadOperation op1;
+  op1.read(0, sizeof(buf), &bl, NULL);
+  op1.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED|LIBRADOS_OP_FLAG_FADVISE_RANDOM);
+  ioctx.aio_operate("test_obj", my_completion2.get(), &op1, 0);
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion2->wait_for_complete());
+  }
+  EXPECT_EQ(0, my_completion2->get_return_value());
+  ASSERT_EQ(0, memcmp(buf, bl.c_str(), sizeof(buf)));
+
+  ioctx.remove("test_obj");
+  destroy_one_pool_pp(pool_name, cluster);
+}
+
+TEST(LibRadosAio, RoundTripAppendPP) {
+  AioTestDataPP test_data;
+  ASSERT_EQ("", test_data.init());
+  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  AioCompletion *my_completion_null = NULL;
+  ASSERT_NE(my_completion, my_completion_null);
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_append("foo", my_completion,
+                                           bl1, sizeof(buf)));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion->get_return_value());
+  char buf2[128];
+  memset(buf2, 0xdd, sizeof(buf2));
+  bufferlist bl2;
+  bl2.append(buf2, sizeof(buf2));
+  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  ASSERT_NE(my_completion2, my_completion_null);
+  ASSERT_EQ(0, test_data.m_ioctx.aio_append("foo", my_completion2,
+                                           bl2, sizeof(buf2)));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion2->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion2->get_return_value());
+  bufferlist bl3;
+  AioCompletion *my_completion3 = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  ASSERT_NE(my_completion3, my_completion_null);
+  ASSERT_EQ(0, test_data.m_ioctx.aio_read("foo",
+                             my_completion3, &bl3, 2 * sizeof(buf), 0));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion3->wait_for_complete());
+  }
+  ASSERT_EQ((int)(sizeof(buf) * 2), my_completion3->get_return_value());
+  ASSERT_EQ(sizeof(buf) * 2, bl3.length());
+  ASSERT_EQ(0, memcmp(bl3.c_str(), buf, sizeof(buf)));
+  ASSERT_EQ(0, memcmp(bl3.c_str() + sizeof(buf), buf2, sizeof(buf2)));
+  delete my_completion;
+  delete my_completion2;
+  delete my_completion3;
+}
+
+TEST(LibRadosAioPP, RemoveTestPP) {
+  char buf[128];
+  memset(buf, 0xaa, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  AioTestDataPP test_data;
+  ASSERT_EQ("", test_data.init());
+  ASSERT_EQ(0, test_data.m_ioctx.append("foo", bl1, sizeof(buf)));
+  boost::scoped_ptr<AioCompletion> my_completion
+    (test_data.m_cluster.aio_create_completion
+     (nullptr, nullptr, nullptr));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_remove("foo", my_completion.get()));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion->get_return_value());
+  bufferlist bl2;
+  ASSERT_EQ(-ENOENT, test_data.m_ioctx.read("foo", bl2, sizeof(buf), 0));
+}
+
+TEST(LibRadosAioEC, RoundTripSparseReadPP) {
+  AioTestDataECPP test_data;
+  ASSERT_EQ("", test_data.init());
+  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  AioCompletion *my_completion_null = NULL;
+  ASSERT_NE(my_completion, my_completion_null);
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
+                                          bl1, sizeof(buf), 0));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion->get_return_value());
+
+  map<uint64_t, uint64_t> extents;
+  bufferlist bl2;
+  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  ASSERT_NE(my_completion2, my_completion_null);
+  ASSERT_EQ(0, test_data.m_ioctx.aio_sparse_read("foo",
+                             my_completion2, &extents, &bl2, sizeof(buf), 0));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion2->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion2->get_return_value());
+  assert_eq_sparse(bl1, extents, bl2);
+  delete my_completion;
+  delete my_completion2;
+}
+
+TEST(LibRadosAioEC, RoundTripAppendPP) {
+  AioTestDataECPP test_data;
+  ASSERT_EQ("", test_data.init());
+  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  AioCompletion *my_completion_null = NULL;
+  ASSERT_NE(my_completion, my_completion_null);
+  bool requires;
+  ASSERT_EQ(0, test_data.m_ioctx.pool_requires_alignment2(&requires));
+  ASSERT_TRUE(requires);
+  uint64_t alignment;
+  ASSERT_EQ(0, test_data.m_ioctx.pool_required_alignment2(&alignment));
+  ASSERT_NE((unsigned)0, alignment);
+  int bsize = alignment;
+  char *buf = (char *)new char[bsize];
+  memset(buf, 0xcc, bsize);
+  bufferlist bl1;
+  bl1.append(buf, bsize);
+  ASSERT_EQ(0, test_data.m_ioctx.aio_append("foo", my_completion,
+                                           bl1, bsize));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion->get_return_value());
+
+  int hbsize = bsize / 2;
+  char *buf2 = (char *)new char[hbsize];
+  memset(buf2, 0xdd, hbsize);
+  bufferlist bl2;
+  bl2.append(buf2, hbsize);
+  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  ASSERT_NE(my_completion2, my_completion_null);
+  ASSERT_EQ(0, test_data.m_ioctx.aio_append("foo", my_completion2,
+                                           bl2, hbsize));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion2->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion2->get_return_value());
+
+  AioCompletion *my_completion3 = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  ASSERT_NE(my_completion3, my_completion_null);
+  ASSERT_EQ(0, test_data.m_ioctx.aio_append("foo", my_completion3,
+                                           bl2, hbsize));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion3->wait_for_complete());
+  }
+  EXPECT_EQ(-EOPNOTSUPP, my_completion3->get_return_value());
+
+  bufferlist bl3;
+  AioCompletion *my_completion4 = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  ASSERT_NE(my_completion4, my_completion_null);
+  ASSERT_EQ(0, test_data.m_ioctx.aio_read("foo",
+                             my_completion4, &bl3, bsize * 3, 0));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion4->wait_for_complete());
+  }
+  int tbsize = bsize + hbsize;
+  ASSERT_EQ(tbsize, my_completion4->get_return_value());
+  ASSERT_EQ((unsigned)tbsize, bl3.length());
+  ASSERT_EQ(0, memcmp(bl3.c_str(), buf, bsize));
+  ASSERT_EQ(0, memcmp(bl3.c_str() + bsize, buf2, hbsize));
+  delete my_completion;
+  delete my_completion2;
+  delete my_completion3;
+  delete[] buf;
+  delete[] buf2;
+}
+
+TEST(LibRadosAioEC, IsCompletePP) {
+  AioTestDataECPP test_data;
+  ASSERT_EQ("", test_data.init());
+  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  AioCompletion *my_completion_null = NULL;
+  ASSERT_NE(my_completion, my_completion_null);
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
+                                          bl1, sizeof(buf), 0));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion->get_return_value());
+  bufferlist bl2;
+  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  ASSERT_NE(my_completion2, my_completion_null);
+  ASSERT_EQ(0, test_data.m_ioctx.aio_read("foo", my_completion2,
+                                         &bl2, sizeof(buf), 0));
+  {
+    TestAlarm alarm;
+
+    // Busy-wait until the AIO completes.
+    // Normally we wouldn't do this, but we want to test is_complete.
+    while (true) {
+      int is_complete = my_completion2->is_complete();
+      if (is_complete)
+       break;
+    }
+  }
+  ASSERT_EQ((int)sizeof(buf), my_completion2->get_return_value());
+  ASSERT_EQ(sizeof(buf), bl2.length());
+  ASSERT_EQ(0, memcmp(buf, bl2.c_str(), sizeof(buf)));
+  delete my_completion;
+  delete my_completion2;
+}
+TEST(LibRadosAioEC, IsSafePP) {
+  AioTestDataECPP test_data;
+  ASSERT_EQ("", test_data.init());
+  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  AioCompletion *my_completion_null = NULL;
+  ASSERT_NE(my_completion, my_completion_null);
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
+                                          bl1, sizeof(buf), 0));
+  {
+    TestAlarm alarm;
+
+    // Busy-wait until the AIO completes.
+    // Normally we wouldn't do this, but we want to test rados_aio_is_safe.
+    while (true) {
+      int is_safe = my_completion->is_safe();
+      if (is_safe)
+       break;
+    }
+  }
+  ASSERT_EQ(0, my_completion->get_return_value());
+  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  bufferlist bl2;
+  ASSERT_NE(my_completion2, my_completion_null);
+  ASSERT_EQ(0, test_data.m_ioctx.aio_read("foo", my_completion2,
+                                         &bl2, sizeof(buf), 0));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion2->wait_for_complete());
+  }
+  ASSERT_EQ((int)sizeof(buf), my_completion2->get_return_value());
+  ASSERT_EQ(sizeof(buf), bl2.length());
+  ASSERT_EQ(0, memcmp(buf, bl2.c_str(), sizeof(buf)));
+  delete my_completion;
+  delete my_completion2;
+}
+
+TEST(LibRadosAioEC, ReturnValuePP) {
+  AioTestDataECPP test_data;
+  ASSERT_EQ("", test_data.init());
+  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  AioCompletion *my_completion_null = NULL;
+  ASSERT_NE(my_completion, my_completion_null);
+  bufferlist bl1;
+  ASSERT_EQ(0, test_data.m_ioctx.aio_read("nonexistent",
+                              my_completion, &bl1, 128, 0));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion->wait_for_complete());
+  }
+  ASSERT_EQ(-ENOENT, my_completion->get_return_value());
+  delete my_completion;
+}
+
+TEST(LibRadosAioEC, FlushPP) {
+  AioTestDataECPP test_data;
+  ASSERT_EQ("", test_data.init());
+  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  AioCompletion *my_completion_null = NULL;
+  ASSERT_NE(my_completion, my_completion_null);
+  char buf[128];
+  memset(buf, 0xee, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
+                                          bl1, sizeof(buf), 0));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_flush());
+  ASSERT_EQ(0, my_completion->get_return_value());
+  bufferlist bl2;
+  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  ASSERT_NE(my_completion2, my_completion_null);
+  ASSERT_EQ(0, test_data.m_ioctx.aio_read("foo", my_completion2,
+                                         &bl2, sizeof(buf), 0));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion2->wait_for_complete());
+  }
+  ASSERT_EQ((int)sizeof(buf), my_completion2->get_return_value());
+  ASSERT_EQ(sizeof(buf), bl2.length());
+  ASSERT_EQ(0, memcmp(buf, bl2.c_str(), sizeof(buf)));
+  delete my_completion;
+  delete my_completion2;
+}
+
+TEST(LibRadosAioEC, FlushAsyncPP) {
+  AioTestDataECPP test_data;
+  ASSERT_EQ("", test_data.init());
+  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  AioCompletion *flush_completion =
+      test_data.m_cluster.aio_create_completion(NULL, NULL, NULL);
+  AioCompletion *my_completion_null = NULL;
+  ASSERT_NE(my_completion, my_completion_null);
+  char buf[128];
+  memset(buf, 0xee, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
+                                          bl1, sizeof(buf), 0));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_flush_async(flush_completion));
+  {
+      TestAlarm alarm;
+      ASSERT_EQ(0, flush_completion->wait_for_complete());
+      ASSERT_EQ(0, flush_completion->wait_for_safe());
+  }
+  ASSERT_EQ(1, my_completion->is_complete());
+  ASSERT_EQ(1, my_completion->is_safe());
+  ASSERT_EQ(1, flush_completion->is_complete());
+  ASSERT_EQ(1, flush_completion->is_safe());
+  ASSERT_EQ(0, my_completion->get_return_value());
+  bufferlist bl2;
+  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  ASSERT_NE(my_completion2, my_completion_null);
+  ASSERT_EQ(0, test_data.m_ioctx.aio_read("foo", my_completion2,
+                                         &bl2, sizeof(buf), 0));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion2->wait_for_complete());
+  }
+  ASSERT_EQ((int)sizeof(buf), my_completion2->get_return_value());
+  ASSERT_EQ(sizeof(buf), bl2.length());
+  ASSERT_EQ(0, memcmp(buf, bl2.c_str(), sizeof(buf)));
+  delete my_completion;
+  delete my_completion2;
+  delete flush_completion;
+}
+
+TEST(LibRadosAioEC, RoundTripWriteFullPP) {
+  AioTestDataECPP test_data;
+  ASSERT_EQ("", test_data.init());
+  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  AioCompletion *my_completion_null = NULL;
+  ASSERT_NE(my_completion, my_completion_null);
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
+                                          bl1, sizeof(buf), 0));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion->get_return_value());
+  char buf2[64];
+  memset(buf2, 0xdd, sizeof(buf2));
+  bufferlist bl2;
+  bl2.append(buf2, sizeof(buf2));
+  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  ASSERT_NE(my_completion2, my_completion_null);
+  ASSERT_EQ(0, test_data.m_ioctx.aio_write_full("foo", my_completion2, bl2));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion2->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion2->get_return_value());
+  bufferlist bl3;
+  AioCompletion *my_completion3 = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  ASSERT_NE(my_completion3, my_completion_null);
+  ASSERT_EQ(0, test_data.m_ioctx.aio_read("foo", my_completion3,
+                                         &bl3, sizeof(buf), 0));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion3->wait_for_complete());
+  }
+  ASSERT_EQ((int)sizeof(buf2), my_completion3->get_return_value());
+  ASSERT_EQ(sizeof(buf2), bl3.length());
+  ASSERT_EQ(0, memcmp(bl3.c_str(), buf2, sizeof(buf2)));
+  delete my_completion;
+  delete my_completion2;
+  delete my_completion3;
+}
+
+//using ObjectWriteOperation/ObjectReadOperation with iohint
+TEST(LibRadosAioEC, RoundTripWriteFullPP2)
+{
+  Rados cluster;
+  std::string pool_name = get_temp_pool_name();
+  ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+  IoCtx ioctx;
+  cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+  boost::scoped_ptr<AioCompletion> my_completion1(cluster.aio_create_completion(0, 0, 0));
+  ObjectWriteOperation op;
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl;
+  bl.append(buf);
+
+  op.write_full(bl);
+  op.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_NOCACHE);
+  ioctx.aio_operate("test_obj", my_completion1.get(), &op);
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion1->wait_for_complete());
+  }
+  EXPECT_EQ(0, my_completion1->get_return_value());
+
+  boost::scoped_ptr<AioCompletion> my_completion2(cluster.aio_create_completion(0, 0, 0));
+  bl.clear();
+  ObjectReadOperation op1;
+  op1.read(0, sizeof(buf), &bl, NULL);
+  op1.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_NOCACHE|LIBRADOS_OP_FLAG_FADVISE_RANDOM);
+  ioctx.aio_operate("test_obj", my_completion2.get(), &op1, 0);
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion2->wait_for_complete());
+  }
+  EXPECT_EQ(0, my_completion2->get_return_value());
+  ASSERT_EQ(0, memcmp(buf, bl.c_str(), sizeof(buf)));
+
+  ioctx.remove("test_obj");
+  destroy_one_pool_pp(pool_name, cluster);
+}
+
+TEST(LibRadosAioEC, SimpleStatPP) {
+  AioTestDataECPP test_data;
+  ASSERT_EQ("", test_data.init());
+  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  AioCompletion *my_completion_null = NULL;
+  ASSERT_NE(my_completion, my_completion_null);
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
+                                          bl1, sizeof(buf), 0));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion->get_return_value());
+  uint64_t psize;
+  time_t pmtime;
+  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  ASSERT_NE(my_completion2, my_completion_null);
+  ASSERT_EQ(0, test_data.m_ioctx.aio_stat("foo", my_completion2,
+                                       &psize, &pmtime));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion2->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion2->get_return_value());
+  ASSERT_EQ(sizeof(buf), psize);
+  delete my_completion;
+  delete my_completion2;
+}
+
+TEST(LibRadosAioEC, SimpleStatPPNS) {
+  AioTestDataECPP test_data;
+  ASSERT_EQ("", test_data.init());
+  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  AioCompletion *my_completion_null = NULL;
+  ASSERT_NE(my_completion, my_completion_null);
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
+                                          bl1, sizeof(buf), 0));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion->get_return_value());
+  uint64_t psize;
+  time_t pmtime;
+  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  ASSERT_NE(my_completion2, my_completion_null);
+  ASSERT_EQ(0, test_data.m_ioctx.aio_stat("foo", my_completion2,
+                                       &psize, &pmtime));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion2->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion2->get_return_value());
+  ASSERT_EQ(sizeof(buf), psize);
+  delete my_completion;
+  delete my_completion2;
+}
+
+TEST(LibRadosAioEC, StatRemovePP) {
+  AioTestDataECPP test_data;
+  ASSERT_EQ("", test_data.init());
+  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  AioCompletion *my_completion_null = NULL;
+  ASSERT_NE(my_completion, my_completion_null);
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
+                                          bl1, sizeof(buf), 0));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion->get_return_value());
+  uint64_t psize;
+  time_t pmtime;
+  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  ASSERT_NE(my_completion2, my_completion_null);
+  ASSERT_EQ(0, test_data.m_ioctx.aio_stat("foo", my_completion2,
+                                       &psize, &pmtime));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion2->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion2->get_return_value());
+  ASSERT_EQ(sizeof(buf), psize);
+  uint64_t psize2;
+  time_t pmtime2;
+  AioCompletion *my_completion3 = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  ASSERT_NE(my_completion3, my_completion_null);
+  ASSERT_EQ(0, test_data.m_ioctx.aio_remove("foo", my_completion3));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion3->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion3->get_return_value());
+
+  AioCompletion *my_completion4 = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  ASSERT_NE(my_completion4, my_completion_null);
+  ASSERT_EQ(0, test_data.m_ioctx.aio_stat("foo", my_completion4,
+                                       &psize2, &pmtime2));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion4->wait_for_complete());
+  }
+  ASSERT_EQ(-ENOENT, my_completion4->get_return_value());
+  delete my_completion;
+  delete my_completion2;
+  delete my_completion3;
+  delete my_completion4;
+}
+
+TEST(LibRadosAioEC, ExecuteClassPP) {
+  AioTestDataECPP test_data;
+  ASSERT_EQ("", test_data.init());
+  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  AioCompletion *my_completion_null = NULL;
+  ASSERT_NE(my_completion, my_completion_null);
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
+                                          bl1, sizeof(buf), 0));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion->get_return_value());
+  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  ASSERT_NE(my_completion2, my_completion_null);
+  bufferlist in, out;
+  ASSERT_EQ(0, test_data.m_ioctx.aio_exec("foo", my_completion2,
+                                         "hello", "say_hello", in, &out));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion2->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion2->get_return_value());
+  ASSERT_EQ(std::string("Hello, world!"), std::string(out.c_str(), out.length()));
+  delete my_completion;
+  delete my_completion2;
+}
+
+TEST(LibRadosAioEC, OmapPP) {
+  Rados cluster;
+  std::string pool_name = get_temp_pool_name();
+  ASSERT_EQ("", create_one_ec_pool_pp(pool_name, cluster));
+  IoCtx ioctx;
+  cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+  string header_str = "baz";
+  bufferptr bp(header_str.c_str(), header_str.size() + 1);
+  bufferlist header_to_set;
+  header_to_set.push_back(bp);
+  map<string, bufferlist> to_set;
+  {
+    boost::scoped_ptr<AioCompletion> my_completion(cluster.aio_create_completion(0, 0, 0));
+    ObjectWriteOperation op;
+    to_set["foo"] = header_to_set;
+    to_set["foo2"] = header_to_set;
+    to_set["qfoo3"] = header_to_set;
+    op.omap_set(to_set);
+
+    op.omap_set_header(header_to_set);
+
+    ioctx.aio_operate("test_obj", my_completion.get(), &op);
+    {
+      TestAlarm alarm;
+      ASSERT_EQ(0, my_completion->wait_for_complete());
+    }
+    EXPECT_EQ(-EOPNOTSUPP, my_completion->get_return_value());
+  }
+  ioctx.remove("test_obj");
+  destroy_one_pool_pp(pool_name, cluster);
+}
+
+TEST(LibRadosAioEC, MultiWritePP) {
+  AioTestDataECPP test_data;
+  ASSERT_EQ("", test_data.init());
+  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  AioCompletion *my_completion_null = NULL;
+  ASSERT_NE(my_completion, my_completion_null);
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
+                                          bl1, sizeof(buf), 0));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion->get_return_value());
+
+  char buf2[64];
+  memset(buf2, 0xdd, sizeof(buf2));
+  bufferlist bl2;
+  bl2.append(buf2, sizeof(buf2));
+  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  ASSERT_NE(my_completion2, my_completion_null);
+  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion2,
+                                          bl2, sizeof(buf2), sizeof(buf)));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion2->wait_for_complete());
+  }
+  ASSERT_EQ(-EOPNOTSUPP, my_completion2->get_return_value());
+
+  bufferlist bl3;
+  AioCompletion *my_completion3 = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  ASSERT_NE(my_completion3, my_completion_null);
+  ASSERT_EQ(0, test_data.m_ioctx.aio_read("foo", my_completion3,
+                                         &bl3, (sizeof(buf) + sizeof(buf2) * 3), 0));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion3->wait_for_complete());
+  }
+  ASSERT_EQ((int)sizeof(buf), my_completion3->get_return_value());
+  ASSERT_EQ(sizeof(buf), bl3.length());
+  ASSERT_EQ(0, memcmp(bl3.c_str(), buf, sizeof(buf)));
+  delete my_completion;
+  delete my_completion2;
+  delete my_completion3;
+}
+
+TEST(LibRadosAio, RacingRemovePP) {
+  AioTestDataPP test_data;
+  ASSERT_EQ("", test_data.init({{"objecter_retry_writes_after_first_reply", "true"}}));
+  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
+        nullptr, nullptr, nullptr);
+  ASSERT_NE(my_completion, nullptr);
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl;
+  bl.append(buf, sizeof(buf));
+  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
+        nullptr, nullptr, nullptr);
+  ASSERT_NE(my_completion2, nullptr);
+  ASSERT_EQ(0, test_data.m_ioctx.aio_remove("foo", my_completion2));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
+                                         bl, sizeof(buf), 0));
+  {
+    TestAlarm alarm;
+    my_completion2->wait_for_complete();
+    my_completion->wait_for_complete();
+  }
+  ASSERT_EQ(-ENOENT, my_completion2->get_return_value());
+  ASSERT_EQ(0, my_completion->get_return_value());
+  ASSERT_EQ(0, test_data.m_ioctx.stat("foo", nullptr, nullptr));
+  delete my_completion;
+  delete my_completion2;
+}
+
+TEST(LibRadosAio, RoundTripCmpExtPP) {
+  AioTestDataPP test_data;
+  ASSERT_EQ("", test_data.init());
+  AioCompletion *my_completion = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  AioCompletion *my_completion_null = NULL;
+  ASSERT_NE(my_completion, my_completion_null);
+  char full[128];
+  memset(full, 0xcc, sizeof(full));
+  bufferlist bl1;
+  bl1.append(full, sizeof(full));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_write("foo", my_completion,
+                                          bl1, sizeof(full), 0));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion->get_return_value());
+
+  /* compare with match */
+  bufferlist cbl;
+  cbl.append(full, sizeof(full));
+  AioCompletion *my_completion2 = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  ASSERT_EQ(0, test_data.m_ioctx.aio_cmpext("foo", my_completion2, 0, cbl));
+
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion2->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion2->get_return_value());
+
+  /* compare with mismatch */
+  memset(full, 0xdd, sizeof(full));
+  cbl.clear();
+  cbl.append(full, sizeof(full));
+  AioCompletion *my_completion3 = test_data.m_cluster.aio_create_completion(
+         nullptr, nullptr, nullptr);
+  ASSERT_EQ(0, test_data.m_ioctx.aio_cmpext("foo", my_completion3, 0, cbl));
+
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion3->wait_for_complete());
+  }
+  ASSERT_EQ(-MAX_ERRNO, my_completion3->get_return_value());
+
+  delete my_completion;
+  delete my_completion2;
+  delete my_completion3;
+}
+
+TEST(LibRadosAio, RoundTripCmpExtPP2)
+{
+  int ret;
+  char buf[128];
+  char miscmp_buf[128];
+  bufferlist cbl;
+  Rados cluster;
+  std::string pool_name = get_temp_pool_name();
+  ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+  IoCtx ioctx;
+  cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+  boost::scoped_ptr<AioCompletion>
+                       wr_cmpl(cluster.aio_create_completion(0, 0, 0));
+  ObjectWriteOperation wr_op;
+  memset(buf, 0xcc, sizeof(buf));
+  memset(miscmp_buf, 0xdd, sizeof(miscmp_buf));
+  bufferlist bl;
+  bl.append(buf, sizeof(buf));
+
+  wr_op.write_full(bl);
+  wr_op.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+  ioctx.aio_operate("test_obj", wr_cmpl.get(), &wr_op);
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, wr_cmpl->wait_for_complete());
+  }
+  EXPECT_EQ(0, wr_cmpl->get_return_value());
+
+  /* cmpext as write op. first match then mismatch */
+  boost::scoped_ptr<AioCompletion>
+                       wr_cmpext_cmpl(cluster.aio_create_completion(0, 0, 0));
+  cbl.append(buf, sizeof(buf));
+  ret = 0;
+
+  wr_op.cmpext(0, cbl, &ret);
+  wr_op.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+  ioctx.aio_operate("test_obj", wr_cmpext_cmpl.get(), &wr_op);
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, wr_cmpext_cmpl->wait_for_complete());
+  }
+  EXPECT_EQ(0, wr_cmpext_cmpl->get_return_value());
+  EXPECT_EQ(0, ret);
+
+  boost::scoped_ptr<AioCompletion>
+                       wr_cmpext_cmpl2(cluster.aio_create_completion(0, 0, 0));
+  cbl.clear();
+  cbl.append(miscmp_buf, sizeof(miscmp_buf));
+  ret = 0;
+
+  wr_op.cmpext(0, cbl, &ret);
+  wr_op.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+  ioctx.aio_operate("test_obj", wr_cmpext_cmpl2.get(), &wr_op);
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, wr_cmpext_cmpl2->wait_for_complete());
+  }
+  EXPECT_EQ(-MAX_ERRNO, wr_cmpext_cmpl2->get_return_value());
+  EXPECT_EQ(-MAX_ERRNO, ret);
+
+  /* cmpext as read op */
+  boost::scoped_ptr<AioCompletion>
+                       rd_cmpext_cmpl(cluster.aio_create_completion(0, 0, 0));
+  ObjectReadOperation rd_op;
+  cbl.clear();
+  cbl.append(buf, sizeof(buf));
+  ret = 0;
+  rd_op.cmpext(0, cbl, &ret);
+  rd_op.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+  ioctx.aio_operate("test_obj", rd_cmpext_cmpl.get(), &rd_op, 0);
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, rd_cmpext_cmpl->wait_for_complete());
+  }
+  EXPECT_EQ(0, rd_cmpext_cmpl->get_return_value());
+  EXPECT_EQ(0, ret);
+
+  boost::scoped_ptr<AioCompletion>
+                       rd_cmpext_cmpl2(cluster.aio_create_completion(0, 0, 0));
+  cbl.clear();
+  cbl.append(miscmp_buf, sizeof(miscmp_buf));
+  ret = 0;
+
+  rd_op.cmpext(0, cbl, &ret);
+  rd_op.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+  ioctx.aio_operate("test_obj", rd_cmpext_cmpl2.get(), &rd_op, 0);
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, rd_cmpext_cmpl2->wait_for_complete());
+  }
+  EXPECT_EQ(-MAX_ERRNO, rd_cmpext_cmpl2->get_return_value());
+  EXPECT_EQ(-MAX_ERRNO, ret);
+
+  ioctx.remove("test_obj");
+  destroy_one_pool_pp(pool_name, cluster);
+}
index 325a2f0a3e0dd0b520705e2effa58b5179c89d6f..558d0942f93f12181a1a88de7ebcb8289535fe9a 100644 (file)
@@ -1,10 +1,10 @@
 // Tests for the C API coverage of atomic write operations
 
 #include <errno.h>
+#include "gtest/gtest.h"
 #include "include/err.h"
 #include "include/rados/librados.h"
 #include "test/librados/test.h"
-#include "gtest/gtest.h"
 
 TEST(LibradosCWriteOps, NewDelete) {
   rados_write_op_t op = rados_create_write_op();
index 10744b2b9b899f6a567695f39ef5a3dc04a5bc4f..c4f24954dbe1196a5d98c16fa60324f63271dffc 100644 (file)
@@ -1,13 +1,13 @@
-#include "include/rados/librados.h"
-#include "include/rados/librados.hpp"
-#include "test/librados/test.h"
-
-#include "gtest/gtest.h"
 #include <errno.h>
 #include <map>
 #include <sstream>
 #include <string>
 
+#include "gtest/gtest.h"
+
+#include "include/rados/librados.hpp"
+#include "test/librados/test_cxx.h"
+
 using namespace librados;
 using std::map;
 using std::ostringstream;
index 043d98328f5e1d6e8cfa25b84b32f4e57ac855e4..49465c3bccea9a3469afcc180f98d40fed518fee 100644 (file)
@@ -13,7 +13,6 @@
 #include <sstream>
 #include <string>
 
-using namespace librados;
 using std::map;
 using std::ostringstream;
 using std::string;
@@ -88,18 +87,6 @@ TEST(LibRadosCmd, MonDescribe) {
   rados_shutdown(cluster);
 }
 
-TEST(LibRadosCmd, MonDescribePP) {
-  Rados cluster;
-  ASSERT_EQ("", connect_cluster_pp(cluster));
-  bufferlist inbl, outbl;
-  string outs;
-  ASSERT_EQ(0, cluster.mon_command("{\"prefix\": \"get_command_descriptions\"}",
-                                  inbl, &outbl, &outs));
-  ASSERT_LT(0u, outbl.length());
-  ASSERT_LE(0u, outs.length());
-  cluster.shutdown();
-}
-
 TEST(LibRadosCmd, OSDCmd) {
   rados_t cluster;
   ASSERT_EQ("", connect_cluster(&cluster));
@@ -128,27 +115,6 @@ TEST(LibRadosCmd, OSDCmd) {
   rados_shutdown(cluster);
 }
 
-TEST(LibRadosCmd, OSDCmdPP) {
-  Rados cluster;
-  ASSERT_EQ("", connect_cluster_pp(cluster));
-  int r;
-  bufferlist inbl, outbl;
-  string outs;
-  string cmd;
-
-  // note: tolerate NXIO here in case the cluster is thrashing out underneath us.
-  cmd = "asdfasdf";
-  r = cluster.osd_command(0, cmd, inbl, &outbl, &outs);
-  ASSERT_TRUE(r == -22 || r == -ENXIO);
-  cmd = "version";
-  r = cluster.osd_command(0, cmd, inbl, &outbl, &outs);
-  ASSERT_TRUE(r == -22 || r == -ENXIO);
-  cmd = "{\"prefix\":\"version\"}";
-  r = cluster.osd_command(0, cmd, inbl, &outbl, &outs);
-  ASSERT_TRUE((r == 0 && outbl.length() > 0) || (r == -ENXIO && outbl.length() == 0));
-  cluster.shutdown();
-}
-
 TEST(LibRadosCmd, PGCmd) {
   rados_t cluster;
   std::string pool_name = get_temp_pool_name();
@@ -193,45 +159,6 @@ TEST(LibRadosCmd, PGCmd) {
   ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster));
 }
 
-TEST(LibRadosCmd, PGCmdPP) {
-  Rados cluster;
-  std::string pool_name = get_temp_pool_name();
-  ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
-
-  int r;
-  bufferlist inbl, outbl;
-  string outs;
-  string cmd;
-
-  int64_t poolid = cluster.pool_lookup(pool_name.c_str());
-  ASSERT_LT(0, poolid);
-
-  string pgid = stringify(poolid) + ".0";
-
-  cmd = "asdfasdf";
-  // note: tolerate NXIO here in case the cluster is thrashing out underneath us.
-  r = cluster.pg_command(pgid.c_str(), cmd, inbl, &outbl, &outs);
-  ASSERT_TRUE(r == -22 || r == -ENXIO);
-
-  // make sure the pg exists on the osd before we query it
-  IoCtx io;
-  cluster.ioctx_create(pool_name.c_str(), io);
-  for (int i=0; i<100; i++) {
-    string oid = "obj" + stringify(i);
-    ASSERT_EQ(-ENOENT, io.stat(oid, NULL, NULL));
-  }
-  io.close();
-
-  cmd = "{\"prefix\":\"pg\", \"cmd\":\"query\", \"pgid\":\"" +  pgid + "\"}";
-  // note: tolerate ENOENT/ENXIO here if hte osd is thrashing out underneath us
-  r = cluster.pg_command(pgid.c_str(), cmd, inbl, &outbl, &outs);
-  ASSERT_TRUE(r == 0 || r == -ENOENT || r == -ENXIO);
-
-  ASSERT_LT(0u, outbl.length());
-
-  ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
-}
-
 struct Log {
   list<string> log;
   std::condition_variable cond;
diff --git a/src/test/librados/cmd_cxx.cc b/src/test/librados/cmd_cxx.cc
new file mode 100644 (file)
index 0000000..d67e261
--- /dev/null
@@ -0,0 +1,92 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <errno.h>
+#include <condition_variable>
+#include <map>
+#include <sstream>
+#include <string>
+
+#include "gtest/gtest.h"
+
+#include "include/rados/librados.hpp"
+#include "include/stringify.h"
+#include "test/librados/test_cxx.h"
+
+using namespace librados;
+using std::map;
+using std::ostringstream;
+using std::string;
+
+TEST(LibRadosCmd, MonDescribePP) {
+  Rados cluster;
+  ASSERT_EQ("", connect_cluster_pp(cluster));
+  bufferlist inbl, outbl;
+  string outs;
+  ASSERT_EQ(0, cluster.mon_command("{\"prefix\": \"get_command_descriptions\"}",
+                                  inbl, &outbl, &outs));
+  ASSERT_LT(0u, outbl.length());
+  ASSERT_LE(0u, outs.length());
+  cluster.shutdown();
+}
+
+TEST(LibRadosCmd, OSDCmdPP) {
+  Rados cluster;
+  ASSERT_EQ("", connect_cluster_pp(cluster));
+  int r;
+  bufferlist inbl, outbl;
+  string outs;
+  string cmd;
+
+  // note: tolerate NXIO here in case the cluster is thrashing out underneath us.
+  cmd = "asdfasdf";
+  r = cluster.osd_command(0, cmd, inbl, &outbl, &outs);
+  ASSERT_TRUE(r == -22 || r == -ENXIO);
+  cmd = "version";
+  r = cluster.osd_command(0, cmd, inbl, &outbl, &outs);
+  ASSERT_TRUE(r == -22 || r == -ENXIO);
+  cmd = "{\"prefix\":\"version\"}";
+  r = cluster.osd_command(0, cmd, inbl, &outbl, &outs);
+  ASSERT_TRUE((r == 0 && outbl.length() > 0) || (r == -ENXIO && outbl.length() == 0));
+  cluster.shutdown();
+}
+
+TEST(LibRadosCmd, PGCmdPP) {
+  Rados cluster;
+  std::string pool_name = get_temp_pool_name();
+  ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+
+  int r;
+  bufferlist inbl, outbl;
+  string outs;
+  string cmd;
+
+  int64_t poolid = cluster.pool_lookup(pool_name.c_str());
+  ASSERT_LT(0, poolid);
+
+  string pgid = stringify(poolid) + ".0";
+
+  cmd = "asdfasdf";
+  // note: tolerate NXIO here in case the cluster is thrashing out underneath us.
+  r = cluster.pg_command(pgid.c_str(), cmd, inbl, &outbl, &outs);
+  ASSERT_TRUE(r == -22 || r == -ENXIO);
+
+  // make sure the pg exists on the osd before we query it
+  IoCtx io;
+  cluster.ioctx_create(pool_name.c_str(), io);
+  for (int i=0; i<100; i++) {
+    string oid = "obj" + stringify(i);
+    ASSERT_EQ(-ENOENT, io.stat(oid, NULL, NULL));
+  }
+  io.close();
+
+  cmd = "{\"prefix\":\"pg\", \"cmd\":\"query\", \"pgid\":\"" +  pgid + "\"}";
+  // note: tolerate ENOENT/ENXIO here if hte osd is thrashing out underneath us
+  r = cluster.pg_command(pgid.c_str(), cmd, inbl, &outbl, &outs);
+  ASSERT_TRUE(r == 0 || r == -ENOENT || r == -ENXIO);
+
+  ASSERT_LT(0u, outbl.length());
+
+  ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}
+
index 9e56c79a603cb8dfd10502177c79d3af8820ebe4..fce3de621226339db93ccd686f6439e46be759c8 100644 (file)
@@ -4,7 +4,6 @@
 #include <climits>
 
 #include "include/rados/librados.h"
-#include "include/rados/librados.hpp"
 #include "include/encoding.h"
 #include "include/err.h"
 #include "include/scope_guard.h"
 #include <errno.h>
 #include "gtest/gtest.h"
 
-using namespace librados;
 using std::string;
 
 typedef RadosTest LibRadosIo;
 typedef RadosTestEC LibRadosIoEC;
-typedef RadosTestPP LibRadosIoPP;
-typedef RadosTestECPP LibRadosIoECPP;
 
 TEST_F(LibRadosIo, SimpleWrite) {
   char buf[128];
@@ -36,12 +32,6 @@ TEST_F(LibRadosIo, TooBig) {
   ASSERT_EQ(-E2BIG, rados_append(ioctx, "A", buf, UINT_MAX));
   ASSERT_EQ(-E2BIG, rados_write_full(ioctx, "A", buf, UINT_MAX));
   ASSERT_EQ(-E2BIG, rados_writesame(ioctx, "A", buf, sizeof(buf), UINT_MAX, 0));
-  IoCtx ioctx;
-  bufferlist bl;
-  ASSERT_EQ(-E2BIG, ioctx.write("foo", bl, UINT_MAX, 0));
-  ASSERT_EQ(-E2BIG, ioctx.append("foo", bl, UINT_MAX));
-  // ioctx.write_full no way to overflow bl.length()
-  ASSERT_EQ(-E2BIG, ioctx.writesame("foo", bl, UINT_MAX, 0));
 }
 
 TEST_F(LibRadosIo, ReadTimeout) {
@@ -96,165 +86,6 @@ TEST_F(LibRadosIo, ReadTimeout) {
   }
 }
 
-TEST_F(LibRadosIoPP, SimpleWritePP) {
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl;
-  bl.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
-  ioctx.set_namespace("nspace");
-  ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
-}
-
-TEST_F(LibRadosIoPP, ReadOpPP) {
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl;
-  bl.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
-
-  {
-      bufferlist op_bl;
-      ObjectReadOperation op;
-      op.read(0, sizeof(buf), NULL, NULL);
-      ASSERT_EQ(0, ioctx.operate("foo", &op, &op_bl));
-      ASSERT_EQ(sizeof(buf), op_bl.length());
-      ASSERT_EQ(0, memcmp(op_bl.c_str(), buf, sizeof(buf)));
-  }
-
-  {
-      bufferlist op_bl;
-      ObjectReadOperation op;
-      op.read(0, 0, NULL, NULL); //len=0 mean read the whole object data.
-      ASSERT_EQ(0, ioctx.operate("foo", &op, &op_bl));
-      ASSERT_EQ(sizeof(buf), op_bl.length());
-      ASSERT_EQ(0, memcmp(op_bl.c_str(), buf, sizeof(buf)));
-  }
-
-  {
-      bufferlist read_bl, op_bl;
-      ObjectReadOperation op;
-      op.read(0, sizeof(buf), &read_bl, NULL);
-      ASSERT_EQ(0, ioctx.operate("foo", &op, &op_bl));
-      ASSERT_EQ(sizeof(buf), read_bl.length());
-      ASSERT_EQ(sizeof(buf), op_bl.length());
-      ASSERT_EQ(0, memcmp(op_bl.c_str(), buf, sizeof(buf)));
-      ASSERT_EQ(0, memcmp(read_bl.c_str(), buf, sizeof(buf)));
-  }
-
-  {
-      bufferlist op_bl;
-      int rval = 1000;
-      ObjectReadOperation op;
-      op.read(0, sizeof(buf), NULL, &rval);
-      ASSERT_EQ(0, ioctx.operate("foo", &op, &op_bl));
-      ASSERT_EQ(sizeof(buf), op_bl.length());
-      ASSERT_EQ(0, rval);
-      ASSERT_EQ(0, memcmp(op_bl.c_str(), buf, sizeof(buf)));
-  }
-
-  {
-      bufferlist read_bl, op_bl;
-      int rval = 1000;
-      ObjectReadOperation op;
-      op.read(0, sizeof(buf), &read_bl, &rval);
-      ASSERT_EQ(0, ioctx.operate("foo", &op, &op_bl));
-      ASSERT_EQ(sizeof(buf), read_bl.length());
-      ASSERT_EQ(sizeof(buf), op_bl.length());
-      ASSERT_EQ(0, rval);
-      ASSERT_EQ(0, memcmp(op_bl.c_str(), buf, sizeof(buf)));
-      ASSERT_EQ(0, memcmp(read_bl.c_str(), buf, sizeof(buf)));
-  }
-
-  {
-      bufferlist read_bl1, read_bl2, op_bl;
-      int rval1 = 1000, rval2 = 1002;
-      ObjectReadOperation op;
-      op.read(0, sizeof(buf), &read_bl1, &rval1);
-      op.read(0, sizeof(buf), &read_bl2, &rval2);
-      ASSERT_EQ(0, ioctx.operate("foo", &op, &op_bl));
-      ASSERT_EQ(sizeof(buf), read_bl1.length());
-      ASSERT_EQ(sizeof(buf), read_bl2.length());
-      ASSERT_EQ(sizeof(buf) * 2, op_bl.length());
-      ASSERT_EQ(0, rval1);
-      ASSERT_EQ(0, rval2);
-      ASSERT_EQ(0, memcmp(read_bl1.c_str(), buf, sizeof(buf)));
-      ASSERT_EQ(0, memcmp(read_bl2.c_str(), buf, sizeof(buf)));
-      ASSERT_EQ(0, memcmp(op_bl.c_str(), buf, sizeof(buf)));
-      ASSERT_EQ(0, memcmp(op_bl.c_str() + sizeof(buf), buf, sizeof(buf)));
-  }
-
-  {
-      bufferlist op_bl;
-      ObjectReadOperation op;
-      op.read(0, sizeof(buf), NULL, NULL);
-      ASSERT_EQ(0, ioctx.operate("foo", &op, &op_bl));
-      ASSERT_EQ(sizeof(buf), op_bl.length());
-      ASSERT_EQ(0, memcmp(op_bl.c_str(), buf, sizeof(buf)));
-  }
-
-  {
-      bufferlist read_bl;
-      ObjectReadOperation op;
-      op.read(0, sizeof(buf), &read_bl, NULL);
-      ASSERT_EQ(0, ioctx.operate("foo", &op, NULL));
-      ASSERT_EQ(sizeof(buf), read_bl.length());
-      ASSERT_EQ(0, memcmp(read_bl.c_str(), buf, sizeof(buf)));
-  }
-
-  {
-      int rval = 1000;
-      ObjectReadOperation op;
-      op.read(0, sizeof(buf), NULL, &rval);
-      ASSERT_EQ(0, ioctx.operate("foo", &op, NULL));
-      ASSERT_EQ(0, rval);
-  }
-
-  {
-      bufferlist read_bl;
-      int rval = 1000;
-      ObjectReadOperation op;
-      op.read(0, sizeof(buf), &read_bl, &rval);
-      ASSERT_EQ(0, ioctx.operate("foo", &op, NULL));
-      ASSERT_EQ(sizeof(buf), read_bl.length());
-      ASSERT_EQ(0, rval);
-      ASSERT_EQ(0, memcmp(read_bl.c_str(), buf, sizeof(buf)));
-  }
-
-  {
-      bufferlist read_bl1, read_bl2;
-      int rval1 = 1000, rval2 = 1002;
-      ObjectReadOperation op;
-      op.read(0, sizeof(buf), &read_bl1, &rval1);
-      op.read(0, sizeof(buf), &read_bl2, &rval2);
-      ASSERT_EQ(0, ioctx.operate("foo", &op, NULL));
-      ASSERT_EQ(sizeof(buf), read_bl1.length());
-      ASSERT_EQ(sizeof(buf), read_bl2.length());
-      ASSERT_EQ(0, rval1);
-      ASSERT_EQ(0, rval2);
-      ASSERT_EQ(0, memcmp(read_bl1.c_str(), buf, sizeof(buf)));
-      ASSERT_EQ(0, memcmp(read_bl2.c_str(), buf, sizeof(buf)));
-  }
-}
-
-TEST_F(LibRadosIoPP, SparseReadOpPP) {
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl;
-  bl.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
-
-  {
-    std::map<uint64_t, uint64_t> extents;
-    bufferlist read_bl;
-    int rval = -1;
-    ObjectReadOperation op;
-    op.sparse_read(0, sizeof(buf), &extents, &read_bl, &rval);
-    ASSERT_EQ(0, ioctx.operate("foo", &op, nullptr));
-    ASSERT_EQ(0, rval);
-    assert_eq_sparse(bl, extents, read_bl);
-  }
-}
 
 TEST_F(LibRadosIo, RoundTrip) {
   char buf[128];
@@ -273,34 +104,6 @@ TEST_F(LibRadosIo, RoundTrip) {
   ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf)));
 }
 
-TEST_F(LibRadosIoPP, RoundTripPP) {
-  char buf[128];
-  Rados cluster;
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl;
-  bl.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
-  bufferlist cl;
-  ASSERT_EQ((int)sizeof(buf), ioctx.read("foo", cl, sizeof(buf), 0));
-  ASSERT_EQ(0, memcmp(buf, cl.c_str(), sizeof(buf)));
-}
-
-TEST_F(LibRadosIoPP, RoundTripPP2)
-{
-  bufferlist bl;
-  bl.append("ceph");
-  ObjectWriteOperation write;
-  write.write(0, bl);
-  write.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
-  ASSERT_EQ(0, ioctx.operate("foo", &write));
-
-  ObjectReadOperation read;
-  read.read(0, bl.length(), NULL, NULL);
-  read.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_NOCACHE|LIBRADOS_OP_FLAG_FADVISE_RANDOM);
-  ASSERT_EQ(0, ioctx.operate("foo", &read, &bl));
-  ASSERT_EQ(0, memcmp(bl.c_str(), "ceph", 4));
-}
-
 TEST_F(LibRadosIo, Checksum) {
   char buf[128];
   memset(buf, 0xcc, sizeof(buf));
@@ -318,27 +121,6 @@ TEST_F(LibRadosIo, Checksum) {
   ASSERT_EQ(expected_crc, crc[1]);
 }
 
-TEST_F(LibRadosIoPP, Checksum) {
-  char buf[128];
-  Rados cluster;
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl;
-  bl.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
-  bufferlist init_value_bl;
-  encode(static_cast<uint32_t>(-1), init_value_bl);
-  bufferlist csum_bl;
-  ASSERT_EQ(0, ioctx.checksum("foo", LIBRADOS_CHECKSUM_TYPE_CRC32C,
-                             init_value_bl, sizeof(buf), 0, 0, &csum_bl));
-  auto csum_bl_it = csum_bl.cbegin();
-  uint32_t csum_count;
-  decode(csum_count, csum_bl_it);
-  ASSERT_EQ(1U, csum_count);
-  uint32_t csum;
-  decode(csum, csum_bl_it);
-  ASSERT_EQ(bl.crc32c(-1), csum);
-}
-
 TEST_F(LibRadosIo, OverlappingWriteRoundTrip) {
   char buf[128];
   char buf2[64];
@@ -353,23 +135,6 @@ TEST_F(LibRadosIo, OverlappingWriteRoundTrip) {
   ASSERT_EQ(0, memcmp(buf3 + sizeof(buf2), buf, sizeof(buf) - sizeof(buf2)));
 }
 
-TEST_F(LibRadosIoPP, OverlappingWriteRoundTripPP) {
-  char buf[128];
-  char buf2[64];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
-  memset(buf2, 0xdd, sizeof(buf2));
-  bufferlist bl2;
-  bl2.append(buf2, sizeof(buf2));
-  ASSERT_EQ(0, ioctx.write("foo", bl2, sizeof(buf2), 0));
-  bufferlist bl3;
-  ASSERT_EQ((int)sizeof(buf), ioctx.read("foo", bl3, sizeof(buf), 0));
-  ASSERT_EQ(0, memcmp(bl3.c_str(), buf2, sizeof(buf2)));
-  ASSERT_EQ(0, memcmp(bl3.c_str() + sizeof(buf2), buf, sizeof(buf) - sizeof(buf2)));
-}
-
 TEST_F(LibRadosIo, WriteFullRoundTrip) {
   char buf[128];
   char buf2[64];
@@ -383,38 +148,6 @@ TEST_F(LibRadosIo, WriteFullRoundTrip) {
   ASSERT_EQ(0, memcmp(buf2, buf3, sizeof(buf2)));
 }
 
-TEST_F(LibRadosIoPP, WriteFullRoundTripPP) {
-  char buf[128];
-  char buf2[64];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
-  memset(buf2, 0xdd, sizeof(buf2));
-  bufferlist bl2;
-  bl2.append(buf2, sizeof(buf2));
-  ASSERT_EQ(0, ioctx.write_full("foo", bl2));
-  bufferlist bl3;
-  ASSERT_EQ((int)sizeof(buf2), ioctx.read("foo", bl3, sizeof(buf), 0));
-  ASSERT_EQ(0, memcmp(bl3.c_str(), buf2, sizeof(buf2)));
-}
-
-TEST_F(LibRadosIoPP, WriteFullRoundTripPP2)
-{
-  bufferlist bl;
-  bl.append("ceph");
-  ObjectWriteOperation write;
-  write.write_full(bl);
-  write.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_NOCACHE);
-  ASSERT_EQ(0, ioctx.operate("foo", &write));
-
-  ObjectReadOperation read;
-  read.read(0, bl.length(), NULL, NULL);
-  read.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED|LIBRADOS_OP_FLAG_FADVISE_RANDOM);
-  ASSERT_EQ(0, ioctx.operate("foo", &read, &bl));
-  ASSERT_EQ(0, memcmp(bl.c_str(), "ceph", 4));
-}
-
 TEST_F(LibRadosIo, AppendRoundTrip) {
   char buf[64];
   char buf2[64];
@@ -429,25 +162,6 @@ TEST_F(LibRadosIo, AppendRoundTrip) {
   ASSERT_EQ(0, memcmp(buf3 + sizeof(buf), buf2, sizeof(buf2)));
 }
 
-TEST_F(LibRadosIoPP, AppendRoundTripPP) {
-  char buf[64];
-  char buf2[64];
-  memset(buf, 0xde, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.append("foo", bl1, sizeof(buf)));
-  memset(buf2, 0xad, sizeof(buf2));
-  bufferlist bl2;
-  bl2.append(buf2, sizeof(buf2));
-  ASSERT_EQ(0, ioctx.append("foo", bl2, sizeof(buf2)));
-  bufferlist bl3;
-  ASSERT_EQ((int)(sizeof(buf) + sizeof(buf2)),
-           ioctx.read("foo", bl3, (sizeof(buf) + sizeof(buf2)), 0));
-  const char *bl3_str = bl3.c_str();
-  ASSERT_EQ(0, memcmp(bl3_str, buf, sizeof(buf)));
-  ASSERT_EQ(0, memcmp(bl3_str + sizeof(buf), buf2, sizeof(buf2)));
-}
-
 TEST_F(LibRadosIo, TruncTest) {
   char buf[128];
   char buf2[sizeof(buf)];
@@ -459,18 +173,6 @@ TEST_F(LibRadosIo, TruncTest) {
   ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf)/2));
 }
 
-TEST_F(LibRadosIoPP, TruncTestPP) {
-  char buf[128];
-  memset(buf, 0xaa, sizeof(buf));
-  bufferlist bl;
-  bl.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.append("foo", bl, sizeof(buf)));
-  ASSERT_EQ(0, ioctx.trunc("foo", sizeof(buf) / 2));
-  bufferlist bl2;
-  ASSERT_EQ((int)(sizeof(buf)/2), ioctx.read("foo", bl2, sizeof(buf), 0));
-  ASSERT_EQ(0, memcmp(bl2.c_str(), buf, sizeof(buf)/2));
-}
-
 TEST_F(LibRadosIo, RemoveTest) {
   char buf[128];
   char buf2[sizeof(buf)];
@@ -481,17 +183,6 @@ TEST_F(LibRadosIo, RemoveTest) {
   ASSERT_EQ(-ENOENT, rados_read(ioctx, "foo", buf2, sizeof(buf2), 0));
 }
 
-TEST_F(LibRadosIoPP, RemoveTestPP) {
-  char buf[128];
-  memset(buf, 0xaa, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.append("foo", bl1, sizeof(buf)));
-  ASSERT_EQ(0, ioctx.remove("foo"));
-  bufferlist bl2;
-  ASSERT_EQ(-ENOENT, ioctx.read("foo", bl2, sizeof(buf), 0));
-}
-
 TEST_F(LibRadosIo, XattrsRoundTrip) {
   char buf[128];
   char attr1[] = "attr1";
@@ -505,25 +196,6 @@ TEST_F(LibRadosIo, XattrsRoundTrip) {
   ASSERT_EQ(0, memcmp(attr1_buf, buf, sizeof(attr1_buf)));
 }
 
-TEST_F(LibRadosIoPP, XattrsRoundTripPP) {
-  char buf[128];
-  char attr1[] = "attr1";
-  char attr1_buf[] = "foo bar baz";
-  memset(buf, 0xaa, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.append("foo", bl1, sizeof(buf)));
-  bufferlist bl2;
-  ASSERT_EQ(-ENODATA, ioctx.getxattr("foo", attr1, bl2));
-  bufferlist bl3;
-  bl3.append(attr1_buf, sizeof(attr1_buf));
-  ASSERT_EQ(0, ioctx.setxattr("foo", attr1, bl3));
-  bufferlist bl4;
-  ASSERT_EQ((int)sizeof(attr1_buf),
-      ioctx.getxattr("foo", attr1, bl4));
-  ASSERT_EQ(0, memcmp(bl4.c_str(), attr1_buf, sizeof(attr1_buf)));
-}
-
 TEST_F(LibRadosIo, RmXattr) {
   char buf[128];
   char attr1[] = "attr1";
@@ -547,36 +219,6 @@ TEST_F(LibRadosIo, RmXattr) {
   ASSERT_EQ(-ENOENT, rados_rmxattr(ioctx, "foo_rmxattr", attr2));
 }
 
-TEST_F(LibRadosIoPP, RmXattrPP) {
-  char buf[128];
-  char attr1[] = "attr1";
-  char attr1_buf[] = "foo bar baz";
-  memset(buf, 0xaa, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.append("foo", bl1, sizeof(buf)));
-  bufferlist bl2;
-  bl2.append(attr1_buf, sizeof(attr1_buf));
-  ASSERT_EQ(0, ioctx.setxattr("foo", attr1, bl2));
-  ASSERT_EQ(0, ioctx.rmxattr("foo", attr1));
-  bufferlist bl3;
-  ASSERT_EQ(-ENODATA, ioctx.getxattr("foo", attr1, bl3));
-
-  // Test rmxattr on a removed object
-  char buf2[128];
-  char attr2[] = "attr2";
-  char attr2_buf[] = "foo bar baz";
-  memset(buf2, 0xbb, sizeof(buf2));
-  bufferlist bl21;
-  bl21.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.write("foo_rmxattr", bl21, sizeof(buf2), 0));
-  bufferlist bl22;
-  bl22.append(attr2_buf, sizeof(attr2_buf));
-  ASSERT_EQ(0, ioctx.setxattr("foo_rmxattr", attr2, bl22));
-  ASSERT_EQ(0, ioctx.remove("foo_rmxattr"));
-  ASSERT_EQ(-ENOENT, ioctx.rmxattr("foo_rmxattr", attr2));
-}
-
 TEST_F(LibRadosIo, XattrIter) {
   char buf[128];
   char attr1[] = "attr1";
@@ -617,41 +259,6 @@ TEST_F(LibRadosIo, XattrIter) {
   rados_getxattrs_end(iter);
 }
 
-TEST_F(LibRadosIoPP, XattrListPP) {
-  char buf[128];
-  char attr1[] = "attr1";
-  char attr1_buf[] = "foo bar baz";
-  char attr2[] = "attr2";
-  char attr2_buf[256];
-  for (size_t j = 0; j < sizeof(attr2_buf); ++j) {
-    attr2_buf[j] = j % 0xff;
-  }
-  memset(buf, 0xaa, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.append("foo", bl1, sizeof(buf)));
-  bufferlist bl2;
-  bl2.append(attr1_buf, sizeof(attr1_buf));
-  ASSERT_EQ(0, ioctx.setxattr("foo", attr1, bl2));
-  bufferlist bl3;
-  bl3.append(attr2_buf, sizeof(attr2_buf));
-  ASSERT_EQ(0, ioctx.setxattr("foo", attr2, bl3));
-  std::map<std::string, bufferlist> attrset;
-  ASSERT_EQ(0, ioctx.getxattrs("foo", attrset));
-  for (std::map<std::string, bufferlist>::iterator i = attrset.begin();
-       i != attrset.end(); ++i) {
-    if (i->first == string(attr1)) {
-      ASSERT_EQ(0, memcmp(i->second.c_str(), attr1_buf, sizeof(attr1_buf)));
-    }
-    else if (i->first == string(attr2)) {
-      ASSERT_EQ(0, memcmp(i->second.c_str(), attr2_buf, sizeof(attr2_buf)));
-    }
-    else {
-      ASSERT_EQ(0, 1);
-    }
-  }
-}
-
 TEST_F(LibRadosIoEC, SimpleWrite) {
   char buf[128];
   memset(buf, 0xcc, sizeof(buf));
@@ -660,166 +267,6 @@ TEST_F(LibRadosIoEC, SimpleWrite) {
   ASSERT_EQ(0, rados_write(ioctx, "foo", buf, sizeof(buf), 0));
 }
 
-TEST_F(LibRadosIoECPP, SimpleWritePP) {
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl;
-  bl.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
-  ioctx.set_namespace("nspace");
-  ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
-}
-
-TEST_F(LibRadosIoECPP, ReadOpPP) {
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl;
-  bl.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
-
-  {
-      bufferlist op_bl;
-      ObjectReadOperation op;
-      op.read(0, sizeof(buf), NULL, NULL);
-      ASSERT_EQ(0, ioctx.operate("foo", &op, &op_bl));
-      ASSERT_EQ(sizeof(buf), op_bl.length());
-      ASSERT_EQ(0, memcmp(op_bl.c_str(), buf, sizeof(buf)));
-  }
-
-  {
-    bufferlist op_bl;
-    ObjectReadOperation op;
-    op.read(0, 0, NULL, NULL); //len=0 mean read the whole object data
-    ASSERT_EQ(0, ioctx.operate("foo", &op, &op_bl));
-    ASSERT_EQ(sizeof(buf), op_bl.length());
-    ASSERT_EQ(0, memcmp(op_bl.c_str(), buf, sizeof(buf)));
-  }
-
-  {
-      bufferlist read_bl, op_bl;
-      ObjectReadOperation op;
-      op.read(0, sizeof(buf), &read_bl, NULL);
-      ASSERT_EQ(0, ioctx.operate("foo", &op, &op_bl));
-      ASSERT_EQ(sizeof(buf), read_bl.length());
-      ASSERT_EQ(sizeof(buf), op_bl.length());
-      ASSERT_EQ(0, memcmp(op_bl.c_str(), buf, sizeof(buf)));
-      ASSERT_EQ(0, memcmp(read_bl.c_str(), buf, sizeof(buf)));
-  }
-
-  {
-      bufferlist op_bl;
-      int rval = 1000;
-      ObjectReadOperation op;
-      op.read(0, sizeof(buf), NULL, &rval);
-      ASSERT_EQ(0, ioctx.operate("foo", &op, &op_bl));
-      ASSERT_EQ(sizeof(buf), op_bl.length());
-      ASSERT_EQ(0, rval);
-      ASSERT_EQ(0, memcmp(op_bl.c_str(), buf, sizeof(buf)));
-  }
-
-  {
-      bufferlist read_bl, op_bl;
-      int rval = 1000;
-      ObjectReadOperation op;
-      op.read(0, sizeof(buf), &read_bl, &rval);
-      ASSERT_EQ(0, ioctx.operate("foo", &op, &op_bl));
-      ASSERT_EQ(sizeof(buf), read_bl.length());
-      ASSERT_EQ(sizeof(buf), op_bl.length());
-      ASSERT_EQ(0, rval);
-      ASSERT_EQ(0, memcmp(op_bl.c_str(), buf, sizeof(buf)));
-      ASSERT_EQ(0, memcmp(read_bl.c_str(), buf, sizeof(buf)));
-  }
-
-  {
-      bufferlist read_bl1, read_bl2, op_bl;
-      int rval1 = 1000, rval2 = 1002;
-      ObjectReadOperation op;
-      op.read(0, sizeof(buf), &read_bl1, &rval1);
-      op.read(0, sizeof(buf), &read_bl2, &rval2);
-      ASSERT_EQ(0, ioctx.operate("foo", &op, &op_bl));
-      ASSERT_EQ(sizeof(buf), read_bl1.length());
-      ASSERT_EQ(sizeof(buf), read_bl2.length());
-      ASSERT_EQ(sizeof(buf) * 2, op_bl.length());
-      ASSERT_EQ(0, rval1);
-      ASSERT_EQ(0, rval2);
-      ASSERT_EQ(0, memcmp(read_bl1.c_str(), buf, sizeof(buf)));
-      ASSERT_EQ(0, memcmp(read_bl2.c_str(), buf, sizeof(buf)));
-      ASSERT_EQ(0, memcmp(op_bl.c_str(), buf, sizeof(buf)));
-      ASSERT_EQ(0, memcmp(op_bl.c_str() + sizeof(buf), buf, sizeof(buf)));
-  }
-
-  {
-      bufferlist op_bl;
-      ObjectReadOperation op;
-      op.read(0, sizeof(buf), NULL, NULL);
-      ASSERT_EQ(0, ioctx.operate("foo", &op, &op_bl));
-      ASSERT_EQ(sizeof(buf), op_bl.length());
-      ASSERT_EQ(0, memcmp(op_bl.c_str(), buf, sizeof(buf)));
-  }
-
-  {
-      bufferlist read_bl;
-      ObjectReadOperation op;
-      op.read(0, sizeof(buf), &read_bl, NULL);
-      ASSERT_EQ(0, ioctx.operate("foo", &op, NULL));
-      ASSERT_EQ(sizeof(buf), read_bl.length());
-      ASSERT_EQ(0, memcmp(read_bl.c_str(), buf, sizeof(buf)));
-  }
-
-  {
-      int rval = 1000;
-      ObjectReadOperation op;
-      op.read(0, sizeof(buf), NULL, &rval);
-      ASSERT_EQ(0, ioctx.operate("foo", &op, NULL));
-      ASSERT_EQ(0, rval);
-  }
-
-  {
-      bufferlist read_bl;
-      int rval = 1000;
-      ObjectReadOperation op;
-      op.read(0, sizeof(buf), &read_bl, &rval);
-      ASSERT_EQ(0, ioctx.operate("foo", &op, NULL));
-      ASSERT_EQ(sizeof(buf), read_bl.length());
-      ASSERT_EQ(0, rval);
-      ASSERT_EQ(0, memcmp(read_bl.c_str(), buf, sizeof(buf)));
-  }
-
-  {
-      bufferlist read_bl1, read_bl2;
-      int rval1 = 1000, rval2 = 1002;
-      ObjectReadOperation op;
-      op.read(0, sizeof(buf), &read_bl1, &rval1);
-      op.read(0, sizeof(buf), &read_bl2, &rval2);
-      ASSERT_EQ(0, ioctx.operate("foo", &op, NULL));
-      ASSERT_EQ(sizeof(buf), read_bl1.length());
-      ASSERT_EQ(sizeof(buf), read_bl2.length());
-      ASSERT_EQ(0, rval1);
-      ASSERT_EQ(0, rval2);
-      ASSERT_EQ(0, memcmp(read_bl1.c_str(), buf, sizeof(buf)));
-      ASSERT_EQ(0, memcmp(read_bl2.c_str(), buf, sizeof(buf)));
-  }
-}
-
-TEST_F(LibRadosIoECPP, SparseReadOpPP) {
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl;
-  bl.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
-
-  {
-    std::map<uint64_t, uint64_t> extents;
-    bufferlist read_bl;
-    int rval = -1;
-    ObjectReadOperation op;
-    op.sparse_read(0, sizeof(buf), &extents, &read_bl, &rval);
-    ASSERT_EQ(0, ioctx.operate("foo", &op, nullptr));
-    ASSERT_EQ(0, rval);
-    assert_eq_sparse(bl, extents, read_bl);
-  }
-}
-
 TEST_F(LibRadosIoEC, RoundTrip) {
   char buf[128];
   char buf2[128];
@@ -833,34 +280,6 @@ TEST_F(LibRadosIoEC, RoundTrip) {
   ASSERT_EQ(-EOPNOTSUPP, rados_write(ioctx, "bar", buf, sizeof(buf), off));
 }
 
-TEST_F(LibRadosIoECPP, RoundTripPP) {
-  char buf[128];
-  Rados cluster;
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl;
-  bl.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
-  bufferlist cl;
-  ASSERT_EQ((int)sizeof(buf), ioctx.read("foo", cl, sizeof(buf) * 3, 0));
-  ASSERT_EQ(0, memcmp(buf, cl.c_str(), sizeof(buf)));
-}
-
-TEST_F(LibRadosIoECPP, RoundTripPP2)
-{
-  bufferlist bl;
-  bl.append("ceph");
-  ObjectWriteOperation write;
-  write.write(0, bl);
-  write.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
-  ASSERT_EQ(0, ioctx.operate("foo", &write));
-
-  ObjectReadOperation read;
-  read.read(0, bl.length(), NULL, NULL);
-  read.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED|LIBRADOS_OP_FLAG_FADVISE_RANDOM);
-  ASSERT_EQ(0, ioctx.operate("foo", &read, &bl));
-  ASSERT_EQ(0, memcmp(bl.c_str(), "ceph", 4));
-}
-
 TEST_F(LibRadosIoEC, OverlappingWriteRoundTrip) {
   int bsize = alignment;
   int dbsize = bsize * 2;
@@ -883,30 +302,6 @@ TEST_F(LibRadosIoEC, OverlappingWriteRoundTrip) {
   ASSERT_EQ(0, memcmp(buf3, buf, dbsize));
 }
 
-TEST_F(LibRadosIoECPP, OverlappingWriteRoundTripPP) {
-  int bsize = alignment;
-  int dbsize = bsize * 2;
-  char *buf = (char *)new char[dbsize];
-  char *buf2 = (char *)new char[bsize];
-  auto cleanup = [&] {
-    delete[] buf;
-    delete[] buf2;
-  };
-  scope_guard<decltype(cleanup)> sg(std::move(cleanup));
-  memset(buf, 0xcc, dbsize);
-  bufferlist bl1;
-  bl1.append(buf, dbsize);
-  ASSERT_EQ(0, ioctx.write("foo", bl1, dbsize, 0));
-  memset(buf2, 0xdd, bsize);
-  bufferlist bl2;
-  bl2.append(buf2, bsize);
-  ASSERT_EQ(-EOPNOTSUPP, ioctx.write("foo", bl2, bsize, 0));
-  bufferlist bl3;
-  ASSERT_EQ(dbsize, ioctx.read("foo", bl3, dbsize, 0));
-  // Read the same as first write
-  ASSERT_EQ(0, memcmp(bl3.c_str(), buf, dbsize));
-}
-
 TEST_F(LibRadosIoEC, WriteFullRoundTrip) {
   char buf[128];
   char buf2[64];
@@ -920,38 +315,6 @@ TEST_F(LibRadosIoEC, WriteFullRoundTrip) {
   ASSERT_EQ(0, memcmp(buf3, buf2, sizeof(buf2)));
 }
 
-TEST_F(LibRadosIoECPP, WriteFullRoundTripPP) {
-  char buf[128];
-  char buf2[64];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
-  memset(buf2, 0xdd, sizeof(buf2));
-  bufferlist bl2;
-  bl2.append(buf2, sizeof(buf2));
-  ASSERT_EQ(0, ioctx.write_full("foo", bl2));
-  bufferlist bl3;
-  ASSERT_EQ((int)sizeof(buf2), ioctx.read("foo", bl3, sizeof(buf), 0));
-  ASSERT_EQ(0, memcmp(bl3.c_str(), buf2, sizeof(buf2)));
-}
-
-TEST_F(LibRadosIoECPP, WriteFullRoundTripPP2)
-{
-  bufferlist bl;
-  bl.append("ceph");
-  ObjectWriteOperation write;
-  write.write_full(bl);
-  write.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
-  ASSERT_EQ(0, ioctx.operate("foo", &write));
-
-  ObjectReadOperation read;
-  read.read(0, bl.length(), NULL, NULL);
-  read.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED|LIBRADOS_OP_FLAG_FADVISE_RANDOM);
-  ASSERT_EQ(0, ioctx.operate("foo", &read, &bl));
-  ASSERT_EQ(0, memcmp(bl.c_str(), "ceph", 4));
-}
-
 TEST_F(LibRadosIoEC, AppendRoundTrip) {
   char *buf = (char *)new char[alignment];
   char *buf2 = (char *)new char[alignment];
@@ -978,30 +341,6 @@ TEST_F(LibRadosIoEC, AppendRoundTrip) {
   ASSERT_EQ(-EOPNOTSUPP, rados_append(ioctx, "foo", unalignedbuf, uasize));
 }
 
-TEST_F(LibRadosIoECPP, AppendRoundTripPP) {
-  char *buf = (char *)new char[alignment];
-  char *buf2 = (char *)new char[alignment];
-  auto cleanup = [&] {
-    delete[] buf;
-    delete[] buf2;
-  };
-  scope_guard<decltype(cleanup)> sg(std::move(cleanup));
-  memset(buf, 0xde, alignment);
-  bufferlist bl1;
-  bl1.append(buf, alignment);
-  ASSERT_EQ(0, ioctx.append("foo", bl1, alignment));
-  memset(buf2, 0xad, alignment);
-  bufferlist bl2;
-  bl2.append(buf2, alignment);
-  ASSERT_EQ(0, ioctx.append("foo", bl2, alignment));
-  bufferlist bl3;
-  ASSERT_EQ((int)(alignment * 2),
-           ioctx.read("foo", bl3, (alignment * 4), 0));
-  const char *bl3_str = bl3.c_str();
-  ASSERT_EQ(0, memcmp(bl3_str, buf, alignment));
-  ASSERT_EQ(0, memcmp(bl3_str + alignment, buf2, alignment));
-}
-
 TEST_F(LibRadosIoEC, TruncTest) {
   char buf[128];
   char buf2[sizeof(buf)];
@@ -1015,20 +354,6 @@ TEST_F(LibRadosIoEC, TruncTest) {
   ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf)));
 }
 
-TEST_F(LibRadosIoECPP, TruncTestPP) {
-  char buf[128];
-  memset(buf, 0xaa, sizeof(buf));
-  bufferlist bl;
-  bl.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.append("foo", bl, sizeof(buf)));
-  ASSERT_EQ(-EOPNOTSUPP, ioctx.trunc("foo", sizeof(buf) / 2));
-  bufferlist bl2;
-  // Same size
-  ASSERT_EQ((int)sizeof(buf), ioctx.read("foo", bl2, sizeof(buf), 0));
-  // No change
-  ASSERT_EQ(0, memcmp(bl2.c_str(), buf, sizeof(buf)));
-}
-
 TEST_F(LibRadosIoEC, RemoveTest) {
   char buf[128];
   char buf2[sizeof(buf)];
@@ -1039,17 +364,6 @@ TEST_F(LibRadosIoEC, RemoveTest) {
   ASSERT_EQ(-ENOENT, rados_read(ioctx, "foo", buf2, sizeof(buf2), 0));
 }
 
-TEST_F(LibRadosIoECPP, RemoveTestPP) {
-  char buf[128];
-  memset(buf, 0xaa, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.append("foo", bl1, sizeof(buf)));
-  ASSERT_EQ(0, ioctx.remove("foo"));
-  bufferlist bl2;
-  ASSERT_EQ(-ENOENT, ioctx.read("foo", bl2, sizeof(buf), 0));
-}
-
 TEST_F(LibRadosIoEC, XattrsRoundTrip) {
   char buf[128];
   char attr1[] = "attr1";
@@ -1063,25 +377,6 @@ TEST_F(LibRadosIoEC, XattrsRoundTrip) {
   ASSERT_EQ(0, memcmp(attr1_buf, buf, sizeof(attr1_buf)));
 }
 
-TEST_F(LibRadosIoECPP, XattrsRoundTripPP) {
-  char buf[128];
-  char attr1[] = "attr1";
-  char attr1_buf[] = "foo bar baz";
-  memset(buf, 0xaa, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.append("foo", bl1, sizeof(buf)));
-  bufferlist bl2;
-  ASSERT_EQ(-ENODATA, ioctx.getxattr("foo", attr1, bl2));
-  bufferlist bl3;
-  bl3.append(attr1_buf, sizeof(attr1_buf));
-  ASSERT_EQ(0, ioctx.setxattr("foo", attr1, bl3));
-  bufferlist bl4;
-  ASSERT_EQ((int)sizeof(attr1_buf),
-      ioctx.getxattr("foo", attr1, bl4));
-  ASSERT_EQ(0, memcmp(bl4.c_str(), attr1_buf, sizeof(attr1_buf)));
-}
-
 TEST_F(LibRadosIoEC, RmXattr) {
   char buf[128];
   char attr1[] = "attr1";
@@ -1105,36 +400,6 @@ TEST_F(LibRadosIoEC, RmXattr) {
   ASSERT_EQ(-ENOENT, rados_rmxattr(ioctx, "foo_rmxattr", attr2));
 }
 
-TEST_F(LibRadosIoECPP, RmXattrPP) {
-  char buf[128];
-  char attr1[] = "attr1";
-  char attr1_buf[] = "foo bar baz";
-  memset(buf, 0xaa, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.append("foo", bl1, sizeof(buf)));
-  bufferlist bl2;
-  bl2.append(attr1_buf, sizeof(attr1_buf));
-  ASSERT_EQ(0, ioctx.setxattr("foo", attr1, bl2));
-  ASSERT_EQ(0, ioctx.rmxattr("foo", attr1));
-  bufferlist bl3;
-  ASSERT_EQ(-ENODATA, ioctx.getxattr("foo", attr1, bl3));
-
-  // Test rmxattr on a removed object
-  char buf2[128];
-  char attr2[] = "attr2";
-  char attr2_buf[] = "foo bar baz";
-  memset(buf2, 0xbb, sizeof(buf2));
-  bufferlist bl21;
-  bl21.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.write("foo_rmxattr", bl21, sizeof(buf2), 0));
-  bufferlist bl22;
-  bl22.append(attr2_buf, sizeof(attr2_buf));
-  ASSERT_EQ(0, ioctx.setxattr("foo_rmxattr", attr2, bl22));
-  ASSERT_EQ(0, ioctx.remove("foo_rmxattr"));
-  ASSERT_EQ(-ENOENT, ioctx.rmxattr("foo_rmxattr", attr2));
-}
-
 TEST_F(LibRadosIoEC, XattrIter) {
   char buf[128];
   char attr1[] = "attr1";
@@ -1174,152 +439,3 @@ TEST_F(LibRadosIoEC, XattrIter) {
   }
   rados_getxattrs_end(iter);
 }
-
-TEST_F(LibRadosIoECPP, XattrListPP) {
-  char buf[128];
-  char attr1[] = "attr1";
-  char attr1_buf[] = "foo bar baz";
-  char attr2[] = "attr2";
-  char attr2_buf[256];
-  for (size_t j = 0; j < sizeof(attr2_buf); ++j) {
-    attr2_buf[j] = j % 0xff;
-  }
-  memset(buf, 0xaa, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.append("foo", bl1, sizeof(buf)));
-  bufferlist bl2;
-  bl2.append(attr1_buf, sizeof(attr1_buf));
-  ASSERT_EQ(0, ioctx.setxattr("foo", attr1, bl2));
-  bufferlist bl3;
-  bl3.append(attr2_buf, sizeof(attr2_buf));
-  ASSERT_EQ(0, ioctx.setxattr("foo", attr2, bl3));
-  std::map<std::string, bufferlist> attrset;
-  ASSERT_EQ(0, ioctx.getxattrs("foo", attrset));
-  for (std::map<std::string, bufferlist>::iterator i = attrset.begin();
-       i != attrset.end(); ++i) {
-    if (i->first == string(attr1)) {
-      ASSERT_EQ(0, memcmp(i->second.c_str(), attr1_buf, sizeof(attr1_buf)));
-    }
-    else if (i->first == string(attr2)) {
-      ASSERT_EQ(0, memcmp(i->second.c_str(), attr2_buf, sizeof(attr2_buf)));
-    }
-    else {
-      ASSERT_EQ(0, 1);
-    }
-  }
-}
-
-TEST_F(LibRadosIoPP, CmpExtPP) {
-  bufferlist bl;
-  bl.append("ceph");
-  ObjectWriteOperation write1;
-  write1.write(0, bl);
-  ASSERT_EQ(0, ioctx.operate("foo", &write1));
-
-  bufferlist new_bl;
-  new_bl.append("CEPH");
-  ObjectWriteOperation write2;
-  write2.cmpext(0, bl, nullptr);
-  write2.write(0, new_bl);
-  ASSERT_EQ(0, ioctx.operate("foo", &write2));
-
-  ObjectReadOperation read;
-  read.read(0, bl.length(), NULL, NULL);
-  ASSERT_EQ(0, ioctx.operate("foo", &read, &bl));
-  ASSERT_EQ(0, memcmp(bl.c_str(), "CEPH", 4));
-}
-
-TEST_F(LibRadosIoPP, CmpExtDNEPP) {
-  bufferlist bl;
-  bl.append(std::string(4, '\0'));
-
-  bufferlist new_bl;
-  new_bl.append("CEPH");
-  ObjectWriteOperation write;
-  write.cmpext(0, bl, nullptr);
-  write.write(0, new_bl);
-  ASSERT_EQ(0, ioctx.operate("foo", &write));
-
-  ObjectReadOperation read;
-  read.read(0, bl.length(), NULL, NULL);
-  ASSERT_EQ(0, ioctx.operate("foo", &read, &bl));
-  ASSERT_EQ(0, memcmp(bl.c_str(), "CEPH", 4));
-}
-
-TEST_F(LibRadosIoPP, CmpExtMismatchPP) {
-  bufferlist bl;
-  bl.append("ceph");
-  ObjectWriteOperation write1;
-  write1.write(0, bl);
-  ASSERT_EQ(0, ioctx.operate("foo", &write1));
-
-  bufferlist new_bl;
-  new_bl.append("CEPH");
-  ObjectWriteOperation write2;
-  write2.cmpext(0, new_bl, nullptr);
-  write2.write(0, new_bl);
-  ASSERT_EQ(-MAX_ERRNO, ioctx.operate("foo", &write2));
-
-  ObjectReadOperation read;
-  read.read(0, bl.length(), NULL, NULL);
-  ASSERT_EQ(0, ioctx.operate("foo", &read, &bl));
-  ASSERT_EQ(0, memcmp(bl.c_str(), "ceph", 4));
-}
-
-TEST_F(LibRadosIoECPP, CmpExtPP) {
-  bufferlist bl;
-  bl.append("ceph");
-  ObjectWriteOperation write1;
-  write1.write(0, bl);
-  ASSERT_EQ(0, ioctx.operate("foo", &write1));
-
-  bufferlist new_bl;
-  new_bl.append("CEPH");
-  ObjectWriteOperation write2;
-  write2.cmpext(0, bl, nullptr);
-  write2.write_full(new_bl);
-  ASSERT_EQ(0, ioctx.operate("foo", &write2));
-
-  ObjectReadOperation read;
-  read.read(0, bl.length(), NULL, NULL);
-  ASSERT_EQ(0, ioctx.operate("foo", &read, &bl));
-  ASSERT_EQ(0, memcmp(bl.c_str(), "CEPH", 4));
-}
-
-TEST_F(LibRadosIoECPP, CmpExtDNEPP) {
-  bufferlist bl;
-  bl.append(std::string(4, '\0'));
-
-  bufferlist new_bl;
-  new_bl.append("CEPH");
-  ObjectWriteOperation write;
-  write.cmpext(0, bl, nullptr);
-  write.write_full(new_bl);
-  ASSERT_EQ(0, ioctx.operate("foo", &write));
-
-  ObjectReadOperation read;
-  read.read(0, bl.length(), NULL, NULL);
-  ASSERT_EQ(0, ioctx.operate("foo", &read, &bl));
-  ASSERT_EQ(0, memcmp(bl.c_str(), "CEPH", 4));
-}
-
-TEST_F(LibRadosIoECPP, CmpExtMismatchPP) {
-  bufferlist bl;
-  bl.append("ceph");
-  ObjectWriteOperation write1;
-  write1.write(0, bl);
-  ASSERT_EQ(0, ioctx.operate("foo", &write1));
-
-  bufferlist new_bl;
-  new_bl.append("CEPH");
-  ObjectWriteOperation write2;
-  write2.cmpext(0, new_bl, nullptr);
-  write2.write_full(new_bl);
-  ASSERT_EQ(-MAX_ERRNO, ioctx.operate("foo", &write2));
-
-  ObjectReadOperation read;
-  read.read(0, bl.length(), NULL, NULL);
-  ASSERT_EQ(0, ioctx.operate("foo", &read, &bl));
-  ASSERT_EQ(0, memcmp(bl.c_str(), "ceph", 4));
-}
diff --git a/src/test/librados/io_cxx.cc b/src/test/librados/io_cxx.cc
new file mode 100644 (file)
index 0000000..5930849
--- /dev/null
@@ -0,0 +1,904 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*
+// vim: ts=8 sw=2 smarttab
+
+#include <climits>
+#include <errno.h>
+
+#include "gtest/gtest.h"
+
+#include "include/rados/librados.hpp"
+#include "include/encoding.h"
+#include "include/err.h"
+#include "include/scope_guard.h"
+#include "test/librados/test_cxx.h"
+#include "test/librados/testcase_cxx.h"
+
+using namespace librados;
+using std::string;
+
+typedef RadosTestPP LibRadosIoPP;
+typedef RadosTestECPP LibRadosIoECPP;
+
+TEST_F(LibRadosIoPP, TooBigPP) {
+  IoCtx ioctx;
+  bufferlist bl;
+  ASSERT_EQ(-E2BIG, ioctx.write("foo", bl, UINT_MAX, 0));
+  ASSERT_EQ(-E2BIG, ioctx.append("foo", bl, UINT_MAX));
+  // ioctx.write_full no way to overflow bl.length()
+  ASSERT_EQ(-E2BIG, ioctx.writesame("foo", bl, UINT_MAX, 0));
+}
+
+TEST_F(LibRadosIoPP, SimpleWritePP) {
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl;
+  bl.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
+  ioctx.set_namespace("nspace");
+  ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
+}
+
+TEST_F(LibRadosIoPP, ReadOpPP) {
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl;
+  bl.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
+
+  {
+      bufferlist op_bl;
+      ObjectReadOperation op;
+      op.read(0, sizeof(buf), NULL, NULL);
+      ASSERT_EQ(0, ioctx.operate("foo", &op, &op_bl));
+      ASSERT_EQ(sizeof(buf), op_bl.length());
+      ASSERT_EQ(0, memcmp(op_bl.c_str(), buf, sizeof(buf)));
+  }
+
+  {
+      bufferlist op_bl;
+      ObjectReadOperation op;
+      op.read(0, 0, NULL, NULL); //len=0 mean read the whole object data.
+      ASSERT_EQ(0, ioctx.operate("foo", &op, &op_bl));
+      ASSERT_EQ(sizeof(buf), op_bl.length());
+      ASSERT_EQ(0, memcmp(op_bl.c_str(), buf, sizeof(buf)));
+  }
+
+  {
+      bufferlist read_bl, op_bl;
+      ObjectReadOperation op;
+      op.read(0, sizeof(buf), &read_bl, NULL);
+      ASSERT_EQ(0, ioctx.operate("foo", &op, &op_bl));
+      ASSERT_EQ(sizeof(buf), read_bl.length());
+      ASSERT_EQ(sizeof(buf), op_bl.length());
+      ASSERT_EQ(0, memcmp(op_bl.c_str(), buf, sizeof(buf)));
+      ASSERT_EQ(0, memcmp(read_bl.c_str(), buf, sizeof(buf)));
+  }
+
+  {
+      bufferlist op_bl;
+      int rval = 1000;
+      ObjectReadOperation op;
+      op.read(0, sizeof(buf), NULL, &rval);
+      ASSERT_EQ(0, ioctx.operate("foo", &op, &op_bl));
+      ASSERT_EQ(sizeof(buf), op_bl.length());
+      ASSERT_EQ(0, rval);
+      ASSERT_EQ(0, memcmp(op_bl.c_str(), buf, sizeof(buf)));
+  }
+
+  {
+      bufferlist read_bl, op_bl;
+      int rval = 1000;
+      ObjectReadOperation op;
+      op.read(0, sizeof(buf), &read_bl, &rval);
+      ASSERT_EQ(0, ioctx.operate("foo", &op, &op_bl));
+      ASSERT_EQ(sizeof(buf), read_bl.length());
+      ASSERT_EQ(sizeof(buf), op_bl.length());
+      ASSERT_EQ(0, rval);
+      ASSERT_EQ(0, memcmp(op_bl.c_str(), buf, sizeof(buf)));
+      ASSERT_EQ(0, memcmp(read_bl.c_str(), buf, sizeof(buf)));
+  }
+
+  {
+      bufferlist read_bl1, read_bl2, op_bl;
+      int rval1 = 1000, rval2 = 1002;
+      ObjectReadOperation op;
+      op.read(0, sizeof(buf), &read_bl1, &rval1);
+      op.read(0, sizeof(buf), &read_bl2, &rval2);
+      ASSERT_EQ(0, ioctx.operate("foo", &op, &op_bl));
+      ASSERT_EQ(sizeof(buf), read_bl1.length());
+      ASSERT_EQ(sizeof(buf), read_bl2.length());
+      ASSERT_EQ(sizeof(buf) * 2, op_bl.length());
+      ASSERT_EQ(0, rval1);
+      ASSERT_EQ(0, rval2);
+      ASSERT_EQ(0, memcmp(read_bl1.c_str(), buf, sizeof(buf)));
+      ASSERT_EQ(0, memcmp(read_bl2.c_str(), buf, sizeof(buf)));
+      ASSERT_EQ(0, memcmp(op_bl.c_str(), buf, sizeof(buf)));
+      ASSERT_EQ(0, memcmp(op_bl.c_str() + sizeof(buf), buf, sizeof(buf)));
+  }
+
+  {
+      bufferlist op_bl;
+      ObjectReadOperation op;
+      op.read(0, sizeof(buf), NULL, NULL);
+      ASSERT_EQ(0, ioctx.operate("foo", &op, &op_bl));
+      ASSERT_EQ(sizeof(buf), op_bl.length());
+      ASSERT_EQ(0, memcmp(op_bl.c_str(), buf, sizeof(buf)));
+  }
+
+  {
+      bufferlist read_bl;
+      ObjectReadOperation op;
+      op.read(0, sizeof(buf), &read_bl, NULL);
+      ASSERT_EQ(0, ioctx.operate("foo", &op, NULL));
+      ASSERT_EQ(sizeof(buf), read_bl.length());
+      ASSERT_EQ(0, memcmp(read_bl.c_str(), buf, sizeof(buf)));
+  }
+
+  {
+      int rval = 1000;
+      ObjectReadOperation op;
+      op.read(0, sizeof(buf), NULL, &rval);
+      ASSERT_EQ(0, ioctx.operate("foo", &op, NULL));
+      ASSERT_EQ(0, rval);
+  }
+
+  {
+      bufferlist read_bl;
+      int rval = 1000;
+      ObjectReadOperation op;
+      op.read(0, sizeof(buf), &read_bl, &rval);
+      ASSERT_EQ(0, ioctx.operate("foo", &op, NULL));
+      ASSERT_EQ(sizeof(buf), read_bl.length());
+      ASSERT_EQ(0, rval);
+      ASSERT_EQ(0, memcmp(read_bl.c_str(), buf, sizeof(buf)));
+  }
+
+  {
+      bufferlist read_bl1, read_bl2;
+      int rval1 = 1000, rval2 = 1002;
+      ObjectReadOperation op;
+      op.read(0, sizeof(buf), &read_bl1, &rval1);
+      op.read(0, sizeof(buf), &read_bl2, &rval2);
+      ASSERT_EQ(0, ioctx.operate("foo", &op, NULL));
+      ASSERT_EQ(sizeof(buf), read_bl1.length());
+      ASSERT_EQ(sizeof(buf), read_bl2.length());
+      ASSERT_EQ(0, rval1);
+      ASSERT_EQ(0, rval2);
+      ASSERT_EQ(0, memcmp(read_bl1.c_str(), buf, sizeof(buf)));
+      ASSERT_EQ(0, memcmp(read_bl2.c_str(), buf, sizeof(buf)));
+  }
+}
+
+TEST_F(LibRadosIoPP, SparseReadOpPP) {
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl;
+  bl.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
+
+  {
+    std::map<uint64_t, uint64_t> extents;
+    bufferlist read_bl;
+    int rval = -1;
+    ObjectReadOperation op;
+    op.sparse_read(0, sizeof(buf), &extents, &read_bl, &rval);
+    ASSERT_EQ(0, ioctx.operate("foo", &op, nullptr));
+    ASSERT_EQ(0, rval);
+    assert_eq_sparse(bl, extents, read_bl);
+  }
+}
+
+TEST_F(LibRadosIoPP, RoundTripPP) {
+  char buf[128];
+  Rados cluster;
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl;
+  bl.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
+  bufferlist cl;
+  ASSERT_EQ((int)sizeof(buf), ioctx.read("foo", cl, sizeof(buf), 0));
+  ASSERT_EQ(0, memcmp(buf, cl.c_str(), sizeof(buf)));
+}
+
+TEST_F(LibRadosIoPP, RoundTripPP2)
+{
+  bufferlist bl;
+  bl.append("ceph");
+  ObjectWriteOperation write;
+  write.write(0, bl);
+  write.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+  ASSERT_EQ(0, ioctx.operate("foo", &write));
+
+  ObjectReadOperation read;
+  read.read(0, bl.length(), NULL, NULL);
+  read.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_NOCACHE|LIBRADOS_OP_FLAG_FADVISE_RANDOM);
+  ASSERT_EQ(0, ioctx.operate("foo", &read, &bl));
+  ASSERT_EQ(0, memcmp(bl.c_str(), "ceph", 4));
+}
+
+TEST_F(LibRadosIoPP, Checksum) {
+  char buf[128];
+  Rados cluster;
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl;
+  bl.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
+  bufferlist init_value_bl;
+  encode(static_cast<uint32_t>(-1), init_value_bl);
+  bufferlist csum_bl;
+  ASSERT_EQ(0, ioctx.checksum("foo", LIBRADOS_CHECKSUM_TYPE_CRC32C,
+                             init_value_bl, sizeof(buf), 0, 0, &csum_bl));
+  auto csum_bl_it = csum_bl.cbegin();
+  uint32_t csum_count;
+  decode(csum_count, csum_bl_it);
+  ASSERT_EQ(1U, csum_count);
+  uint32_t csum;
+  decode(csum, csum_bl_it);
+  ASSERT_EQ(bl.crc32c(-1), csum);
+}
+
+TEST_F(LibRadosIoPP, OverlappingWriteRoundTripPP) {
+  char buf[128];
+  char buf2[64];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+  memset(buf2, 0xdd, sizeof(buf2));
+  bufferlist bl2;
+  bl2.append(buf2, sizeof(buf2));
+  ASSERT_EQ(0, ioctx.write("foo", bl2, sizeof(buf2), 0));
+  bufferlist bl3;
+  ASSERT_EQ((int)sizeof(buf), ioctx.read("foo", bl3, sizeof(buf), 0));
+  ASSERT_EQ(0, memcmp(bl3.c_str(), buf2, sizeof(buf2)));
+  ASSERT_EQ(0, memcmp(bl3.c_str() + sizeof(buf2), buf, sizeof(buf) - sizeof(buf2)));
+}
+
+TEST_F(LibRadosIoPP, WriteFullRoundTripPP) {
+  char buf[128];
+  char buf2[64];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+  memset(buf2, 0xdd, sizeof(buf2));
+  bufferlist bl2;
+  bl2.append(buf2, sizeof(buf2));
+  ASSERT_EQ(0, ioctx.write_full("foo", bl2));
+  bufferlist bl3;
+  ASSERT_EQ((int)sizeof(buf2), ioctx.read("foo", bl3, sizeof(buf), 0));
+  ASSERT_EQ(0, memcmp(bl3.c_str(), buf2, sizeof(buf2)));
+}
+
+TEST_F(LibRadosIoPP, WriteFullRoundTripPP2)
+{
+  bufferlist bl;
+  bl.append("ceph");
+  ObjectWriteOperation write;
+  write.write_full(bl);
+  write.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_NOCACHE);
+  ASSERT_EQ(0, ioctx.operate("foo", &write));
+
+  ObjectReadOperation read;
+  read.read(0, bl.length(), NULL, NULL);
+  read.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED|LIBRADOS_OP_FLAG_FADVISE_RANDOM);
+  ASSERT_EQ(0, ioctx.operate("foo", &read, &bl));
+  ASSERT_EQ(0, memcmp(bl.c_str(), "ceph", 4));
+}
+
+TEST_F(LibRadosIoPP, AppendRoundTripPP) {
+  char buf[64];
+  char buf2[64];
+  memset(buf, 0xde, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.append("foo", bl1, sizeof(buf)));
+  memset(buf2, 0xad, sizeof(buf2));
+  bufferlist bl2;
+  bl2.append(buf2, sizeof(buf2));
+  ASSERT_EQ(0, ioctx.append("foo", bl2, sizeof(buf2)));
+  bufferlist bl3;
+  ASSERT_EQ((int)(sizeof(buf) + sizeof(buf2)),
+           ioctx.read("foo", bl3, (sizeof(buf) + sizeof(buf2)), 0));
+  const char *bl3_str = bl3.c_str();
+  ASSERT_EQ(0, memcmp(bl3_str, buf, sizeof(buf)));
+  ASSERT_EQ(0, memcmp(bl3_str + sizeof(buf), buf2, sizeof(buf2)));
+}
+
+TEST_F(LibRadosIoPP, TruncTestPP) {
+  char buf[128];
+  memset(buf, 0xaa, sizeof(buf));
+  bufferlist bl;
+  bl.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.append("foo", bl, sizeof(buf)));
+  ASSERT_EQ(0, ioctx.trunc("foo", sizeof(buf) / 2));
+  bufferlist bl2;
+  ASSERT_EQ((int)(sizeof(buf)/2), ioctx.read("foo", bl2, sizeof(buf), 0));
+  ASSERT_EQ(0, memcmp(bl2.c_str(), buf, sizeof(buf)/2));
+}
+
+TEST_F(LibRadosIoPP, RemoveTestPP) {
+  char buf[128];
+  memset(buf, 0xaa, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.append("foo", bl1, sizeof(buf)));
+  ASSERT_EQ(0, ioctx.remove("foo"));
+  bufferlist bl2;
+  ASSERT_EQ(-ENOENT, ioctx.read("foo", bl2, sizeof(buf), 0));
+}
+
+TEST_F(LibRadosIoPP, XattrsRoundTripPP) {
+  char buf[128];
+  char attr1[] = "attr1";
+  char attr1_buf[] = "foo bar baz";
+  memset(buf, 0xaa, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.append("foo", bl1, sizeof(buf)));
+  bufferlist bl2;
+  ASSERT_EQ(-ENODATA, ioctx.getxattr("foo", attr1, bl2));
+  bufferlist bl3;
+  bl3.append(attr1_buf, sizeof(attr1_buf));
+  ASSERT_EQ(0, ioctx.setxattr("foo", attr1, bl3));
+  bufferlist bl4;
+  ASSERT_EQ((int)sizeof(attr1_buf),
+      ioctx.getxattr("foo", attr1, bl4));
+  ASSERT_EQ(0, memcmp(bl4.c_str(), attr1_buf, sizeof(attr1_buf)));
+}
+
+TEST_F(LibRadosIoPP, RmXattrPP) {
+  char buf[128];
+  char attr1[] = "attr1";
+  char attr1_buf[] = "foo bar baz";
+  memset(buf, 0xaa, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.append("foo", bl1, sizeof(buf)));
+  bufferlist bl2;
+  bl2.append(attr1_buf, sizeof(attr1_buf));
+  ASSERT_EQ(0, ioctx.setxattr("foo", attr1, bl2));
+  ASSERT_EQ(0, ioctx.rmxattr("foo", attr1));
+  bufferlist bl3;
+  ASSERT_EQ(-ENODATA, ioctx.getxattr("foo", attr1, bl3));
+
+  // Test rmxattr on a removed object
+  char buf2[128];
+  char attr2[] = "attr2";
+  char attr2_buf[] = "foo bar baz";
+  memset(buf2, 0xbb, sizeof(buf2));
+  bufferlist bl21;
+  bl21.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.write("foo_rmxattr", bl21, sizeof(buf2), 0));
+  bufferlist bl22;
+  bl22.append(attr2_buf, sizeof(attr2_buf));
+  ASSERT_EQ(0, ioctx.setxattr("foo_rmxattr", attr2, bl22));
+  ASSERT_EQ(0, ioctx.remove("foo_rmxattr"));
+  ASSERT_EQ(-ENOENT, ioctx.rmxattr("foo_rmxattr", attr2));
+}
+
+TEST_F(LibRadosIoPP, XattrListPP) {
+  char buf[128];
+  char attr1[] = "attr1";
+  char attr1_buf[] = "foo bar baz";
+  char attr2[] = "attr2";
+  char attr2_buf[256];
+  for (size_t j = 0; j < sizeof(attr2_buf); ++j) {
+    attr2_buf[j] = j % 0xff;
+  }
+  memset(buf, 0xaa, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.append("foo", bl1, sizeof(buf)));
+  bufferlist bl2;
+  bl2.append(attr1_buf, sizeof(attr1_buf));
+  ASSERT_EQ(0, ioctx.setxattr("foo", attr1, bl2));
+  bufferlist bl3;
+  bl3.append(attr2_buf, sizeof(attr2_buf));
+  ASSERT_EQ(0, ioctx.setxattr("foo", attr2, bl3));
+  std::map<std::string, bufferlist> attrset;
+  ASSERT_EQ(0, ioctx.getxattrs("foo", attrset));
+  for (std::map<std::string, bufferlist>::iterator i = attrset.begin();
+       i != attrset.end(); ++i) {
+    if (i->first == string(attr1)) {
+      ASSERT_EQ(0, memcmp(i->second.c_str(), attr1_buf, sizeof(attr1_buf)));
+    }
+    else if (i->first == string(attr2)) {
+      ASSERT_EQ(0, memcmp(i->second.c_str(), attr2_buf, sizeof(attr2_buf)));
+    }
+    else {
+      ASSERT_EQ(0, 1);
+    }
+  }
+}
+
+TEST_F(LibRadosIoECPP, SimpleWritePP) {
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl;
+  bl.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
+  ioctx.set_namespace("nspace");
+  ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
+}
+
+TEST_F(LibRadosIoECPP, ReadOpPP) {
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl;
+  bl.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
+
+  {
+      bufferlist op_bl;
+      ObjectReadOperation op;
+      op.read(0, sizeof(buf), NULL, NULL);
+      ASSERT_EQ(0, ioctx.operate("foo", &op, &op_bl));
+      ASSERT_EQ(sizeof(buf), op_bl.length());
+      ASSERT_EQ(0, memcmp(op_bl.c_str(), buf, sizeof(buf)));
+  }
+
+  {
+    bufferlist op_bl;
+    ObjectReadOperation op;
+    op.read(0, 0, NULL, NULL); //len=0 mean read the whole object data
+    ASSERT_EQ(0, ioctx.operate("foo", &op, &op_bl));
+    ASSERT_EQ(sizeof(buf), op_bl.length());
+    ASSERT_EQ(0, memcmp(op_bl.c_str(), buf, sizeof(buf)));
+  }
+
+  {
+      bufferlist read_bl, op_bl;
+      ObjectReadOperation op;
+      op.read(0, sizeof(buf), &read_bl, NULL);
+      ASSERT_EQ(0, ioctx.operate("foo", &op, &op_bl));
+      ASSERT_EQ(sizeof(buf), read_bl.length());
+      ASSERT_EQ(sizeof(buf), op_bl.length());
+      ASSERT_EQ(0, memcmp(op_bl.c_str(), buf, sizeof(buf)));
+      ASSERT_EQ(0, memcmp(read_bl.c_str(), buf, sizeof(buf)));
+  }
+
+  {
+      bufferlist op_bl;
+      int rval = 1000;
+      ObjectReadOperation op;
+      op.read(0, sizeof(buf), NULL, &rval);
+      ASSERT_EQ(0, ioctx.operate("foo", &op, &op_bl));
+      ASSERT_EQ(sizeof(buf), op_bl.length());
+      ASSERT_EQ(0, rval);
+      ASSERT_EQ(0, memcmp(op_bl.c_str(), buf, sizeof(buf)));
+  }
+
+  {
+      bufferlist read_bl, op_bl;
+      int rval = 1000;
+      ObjectReadOperation op;
+      op.read(0, sizeof(buf), &read_bl, &rval);
+      ASSERT_EQ(0, ioctx.operate("foo", &op, &op_bl));
+      ASSERT_EQ(sizeof(buf), read_bl.length());
+      ASSERT_EQ(sizeof(buf), op_bl.length());
+      ASSERT_EQ(0, rval);
+      ASSERT_EQ(0, memcmp(op_bl.c_str(), buf, sizeof(buf)));
+      ASSERT_EQ(0, memcmp(read_bl.c_str(), buf, sizeof(buf)));
+  }
+
+  {
+      bufferlist read_bl1, read_bl2, op_bl;
+      int rval1 = 1000, rval2 = 1002;
+      ObjectReadOperation op;
+      op.read(0, sizeof(buf), &read_bl1, &rval1);
+      op.read(0, sizeof(buf), &read_bl2, &rval2);
+      ASSERT_EQ(0, ioctx.operate("foo", &op, &op_bl));
+      ASSERT_EQ(sizeof(buf), read_bl1.length());
+      ASSERT_EQ(sizeof(buf), read_bl2.length());
+      ASSERT_EQ(sizeof(buf) * 2, op_bl.length());
+      ASSERT_EQ(0, rval1);
+      ASSERT_EQ(0, rval2);
+      ASSERT_EQ(0, memcmp(read_bl1.c_str(), buf, sizeof(buf)));
+      ASSERT_EQ(0, memcmp(read_bl2.c_str(), buf, sizeof(buf)));
+      ASSERT_EQ(0, memcmp(op_bl.c_str(), buf, sizeof(buf)));
+      ASSERT_EQ(0, memcmp(op_bl.c_str() + sizeof(buf), buf, sizeof(buf)));
+  }
+
+  {
+      bufferlist op_bl;
+      ObjectReadOperation op;
+      op.read(0, sizeof(buf), NULL, NULL);
+      ASSERT_EQ(0, ioctx.operate("foo", &op, &op_bl));
+      ASSERT_EQ(sizeof(buf), op_bl.length());
+      ASSERT_EQ(0, memcmp(op_bl.c_str(), buf, sizeof(buf)));
+  }
+
+  {
+      bufferlist read_bl;
+      ObjectReadOperation op;
+      op.read(0, sizeof(buf), &read_bl, NULL);
+      ASSERT_EQ(0, ioctx.operate("foo", &op, NULL));
+      ASSERT_EQ(sizeof(buf), read_bl.length());
+      ASSERT_EQ(0, memcmp(read_bl.c_str(), buf, sizeof(buf)));
+  }
+
+  {
+      int rval = 1000;
+      ObjectReadOperation op;
+      op.read(0, sizeof(buf), NULL, &rval);
+      ASSERT_EQ(0, ioctx.operate("foo", &op, NULL));
+      ASSERT_EQ(0, rval);
+  }
+
+  {
+      bufferlist read_bl;
+      int rval = 1000;
+      ObjectReadOperation op;
+      op.read(0, sizeof(buf), &read_bl, &rval);
+      ASSERT_EQ(0, ioctx.operate("foo", &op, NULL));
+      ASSERT_EQ(sizeof(buf), read_bl.length());
+      ASSERT_EQ(0, rval);
+      ASSERT_EQ(0, memcmp(read_bl.c_str(), buf, sizeof(buf)));
+  }
+
+  {
+      bufferlist read_bl1, read_bl2;
+      int rval1 = 1000, rval2 = 1002;
+      ObjectReadOperation op;
+      op.read(0, sizeof(buf), &read_bl1, &rval1);
+      op.read(0, sizeof(buf), &read_bl2, &rval2);
+      ASSERT_EQ(0, ioctx.operate("foo", &op, NULL));
+      ASSERT_EQ(sizeof(buf), read_bl1.length());
+      ASSERT_EQ(sizeof(buf), read_bl2.length());
+      ASSERT_EQ(0, rval1);
+      ASSERT_EQ(0, rval2);
+      ASSERT_EQ(0, memcmp(read_bl1.c_str(), buf, sizeof(buf)));
+      ASSERT_EQ(0, memcmp(read_bl2.c_str(), buf, sizeof(buf)));
+  }
+}
+
+TEST_F(LibRadosIoECPP, SparseReadOpPP) {
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl;
+  bl.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
+
+  {
+    std::map<uint64_t, uint64_t> extents;
+    bufferlist read_bl;
+    int rval = -1;
+    ObjectReadOperation op;
+    op.sparse_read(0, sizeof(buf), &extents, &read_bl, &rval);
+    ASSERT_EQ(0, ioctx.operate("foo", &op, nullptr));
+    ASSERT_EQ(0, rval);
+    assert_eq_sparse(bl, extents, read_bl);
+  }
+}
+
+TEST_F(LibRadosIoECPP, RoundTripPP) {
+  char buf[128];
+  Rados cluster;
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl;
+  bl.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
+  bufferlist cl;
+  ASSERT_EQ((int)sizeof(buf), ioctx.read("foo", cl, sizeof(buf) * 3, 0));
+  ASSERT_EQ(0, memcmp(buf, cl.c_str(), sizeof(buf)));
+}
+
+TEST_F(LibRadosIoECPP, RoundTripPP2)
+{
+  bufferlist bl;
+  bl.append("ceph");
+  ObjectWriteOperation write;
+  write.write(0, bl);
+  write.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+  ASSERT_EQ(0, ioctx.operate("foo", &write));
+
+  ObjectReadOperation read;
+  read.read(0, bl.length(), NULL, NULL);
+  read.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED|LIBRADOS_OP_FLAG_FADVISE_RANDOM);
+  ASSERT_EQ(0, ioctx.operate("foo", &read, &bl));
+  ASSERT_EQ(0, memcmp(bl.c_str(), "ceph", 4));
+}
+
+TEST_F(LibRadosIoECPP, OverlappingWriteRoundTripPP) {
+  int bsize = alignment;
+  int dbsize = bsize * 2;
+  char *buf = (char *)new char[dbsize];
+  char *buf2 = (char *)new char[bsize];
+  auto cleanup = [&] {
+    delete[] buf;
+    delete[] buf2;
+  };
+  scope_guard<decltype(cleanup)> sg(std::move(cleanup));
+  memset(buf, 0xcc, dbsize);
+  bufferlist bl1;
+  bl1.append(buf, dbsize);
+  ASSERT_EQ(0, ioctx.write("foo", bl1, dbsize, 0));
+  memset(buf2, 0xdd, bsize);
+  bufferlist bl2;
+  bl2.append(buf2, bsize);
+  ASSERT_EQ(-EOPNOTSUPP, ioctx.write("foo", bl2, bsize, 0));
+  bufferlist bl3;
+  ASSERT_EQ(dbsize, ioctx.read("foo", bl3, dbsize, 0));
+  // Read the same as first write
+  ASSERT_EQ(0, memcmp(bl3.c_str(), buf, dbsize));
+}
+
+TEST_F(LibRadosIoECPP, WriteFullRoundTripPP) {
+  char buf[128];
+  char buf2[64];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+  memset(buf2, 0xdd, sizeof(buf2));
+  bufferlist bl2;
+  bl2.append(buf2, sizeof(buf2));
+  ASSERT_EQ(0, ioctx.write_full("foo", bl2));
+  bufferlist bl3;
+  ASSERT_EQ((int)sizeof(buf2), ioctx.read("foo", bl3, sizeof(buf), 0));
+  ASSERT_EQ(0, memcmp(bl3.c_str(), buf2, sizeof(buf2)));
+}
+
+TEST_F(LibRadosIoECPP, WriteFullRoundTripPP2)
+{
+  bufferlist bl;
+  bl.append("ceph");
+  ObjectWriteOperation write;
+  write.write_full(bl);
+  write.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+  ASSERT_EQ(0, ioctx.operate("foo", &write));
+
+  ObjectReadOperation read;
+  read.read(0, bl.length(), NULL, NULL);
+  read.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED|LIBRADOS_OP_FLAG_FADVISE_RANDOM);
+  ASSERT_EQ(0, ioctx.operate("foo", &read, &bl));
+  ASSERT_EQ(0, memcmp(bl.c_str(), "ceph", 4));
+}
+
+TEST_F(LibRadosIoECPP, AppendRoundTripPP) {
+  char *buf = (char *)new char[alignment];
+  char *buf2 = (char *)new char[alignment];
+  auto cleanup = [&] {
+    delete[] buf;
+    delete[] buf2;
+  };
+  scope_guard<decltype(cleanup)> sg(std::move(cleanup));
+  memset(buf, 0xde, alignment);
+  bufferlist bl1;
+  bl1.append(buf, alignment);
+  ASSERT_EQ(0, ioctx.append("foo", bl1, alignment));
+  memset(buf2, 0xad, alignment);
+  bufferlist bl2;
+  bl2.append(buf2, alignment);
+  ASSERT_EQ(0, ioctx.append("foo", bl2, alignment));
+  bufferlist bl3;
+  ASSERT_EQ((int)(alignment * 2),
+           ioctx.read("foo", bl3, (alignment * 4), 0));
+  const char *bl3_str = bl3.c_str();
+  ASSERT_EQ(0, memcmp(bl3_str, buf, alignment));
+  ASSERT_EQ(0, memcmp(bl3_str + alignment, buf2, alignment));
+}
+
+TEST_F(LibRadosIoECPP, TruncTestPP) {
+  char buf[128];
+  memset(buf, 0xaa, sizeof(buf));
+  bufferlist bl;
+  bl.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.append("foo", bl, sizeof(buf)));
+  ASSERT_EQ(-EOPNOTSUPP, ioctx.trunc("foo", sizeof(buf) / 2));
+  bufferlist bl2;
+  // Same size
+  ASSERT_EQ((int)sizeof(buf), ioctx.read("foo", bl2, sizeof(buf), 0));
+  // No change
+  ASSERT_EQ(0, memcmp(bl2.c_str(), buf, sizeof(buf)));
+}
+
+TEST_F(LibRadosIoECPP, RemoveTestPP) {
+  char buf[128];
+  memset(buf, 0xaa, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.append("foo", bl1, sizeof(buf)));
+  ASSERT_EQ(0, ioctx.remove("foo"));
+  bufferlist bl2;
+  ASSERT_EQ(-ENOENT, ioctx.read("foo", bl2, sizeof(buf), 0));
+}
+
+TEST_F(LibRadosIoECPP, XattrsRoundTripPP) {
+  char buf[128];
+  char attr1[] = "attr1";
+  char attr1_buf[] = "foo bar baz";
+  memset(buf, 0xaa, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.append("foo", bl1, sizeof(buf)));
+  bufferlist bl2;
+  ASSERT_EQ(-ENODATA, ioctx.getxattr("foo", attr1, bl2));
+  bufferlist bl3;
+  bl3.append(attr1_buf, sizeof(attr1_buf));
+  ASSERT_EQ(0, ioctx.setxattr("foo", attr1, bl3));
+  bufferlist bl4;
+  ASSERT_EQ((int)sizeof(attr1_buf),
+      ioctx.getxattr("foo", attr1, bl4));
+  ASSERT_EQ(0, memcmp(bl4.c_str(), attr1_buf, sizeof(attr1_buf)));
+}
+
+TEST_F(LibRadosIoECPP, RmXattrPP) {
+  char buf[128];
+  char attr1[] = "attr1";
+  char attr1_buf[] = "foo bar baz";
+  memset(buf, 0xaa, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.append("foo", bl1, sizeof(buf)));
+  bufferlist bl2;
+  bl2.append(attr1_buf, sizeof(attr1_buf));
+  ASSERT_EQ(0, ioctx.setxattr("foo", attr1, bl2));
+  ASSERT_EQ(0, ioctx.rmxattr("foo", attr1));
+  bufferlist bl3;
+  ASSERT_EQ(-ENODATA, ioctx.getxattr("foo", attr1, bl3));
+
+  // Test rmxattr on a removed object
+  char buf2[128];
+  char attr2[] = "attr2";
+  char attr2_buf[] = "foo bar baz";
+  memset(buf2, 0xbb, sizeof(buf2));
+  bufferlist bl21;
+  bl21.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.write("foo_rmxattr", bl21, sizeof(buf2), 0));
+  bufferlist bl22;
+  bl22.append(attr2_buf, sizeof(attr2_buf));
+  ASSERT_EQ(0, ioctx.setxattr("foo_rmxattr", attr2, bl22));
+  ASSERT_EQ(0, ioctx.remove("foo_rmxattr"));
+  ASSERT_EQ(-ENOENT, ioctx.rmxattr("foo_rmxattr", attr2));
+}
+
+TEST_F(LibRadosIoECPP, XattrListPP) {
+  char buf[128];
+  char attr1[] = "attr1";
+  char attr1_buf[] = "foo bar baz";
+  char attr2[] = "attr2";
+  char attr2_buf[256];
+  for (size_t j = 0; j < sizeof(attr2_buf); ++j) {
+    attr2_buf[j] = j % 0xff;
+  }
+  memset(buf, 0xaa, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.append("foo", bl1, sizeof(buf)));
+  bufferlist bl2;
+  bl2.append(attr1_buf, sizeof(attr1_buf));
+  ASSERT_EQ(0, ioctx.setxattr("foo", attr1, bl2));
+  bufferlist bl3;
+  bl3.append(attr2_buf, sizeof(attr2_buf));
+  ASSERT_EQ(0, ioctx.setxattr("foo", attr2, bl3));
+  std::map<std::string, bufferlist> attrset;
+  ASSERT_EQ(0, ioctx.getxattrs("foo", attrset));
+  for (std::map<std::string, bufferlist>::iterator i = attrset.begin();
+       i != attrset.end(); ++i) {
+    if (i->first == string(attr1)) {
+      ASSERT_EQ(0, memcmp(i->second.c_str(), attr1_buf, sizeof(attr1_buf)));
+    }
+    else if (i->first == string(attr2)) {
+      ASSERT_EQ(0, memcmp(i->second.c_str(), attr2_buf, sizeof(attr2_buf)));
+    }
+    else {
+      ASSERT_EQ(0, 1);
+    }
+  }
+}
+
+TEST_F(LibRadosIoPP, CmpExtPP) {
+  bufferlist bl;
+  bl.append("ceph");
+  ObjectWriteOperation write1;
+  write1.write(0, bl);
+  ASSERT_EQ(0, ioctx.operate("foo", &write1));
+
+  bufferlist new_bl;
+  new_bl.append("CEPH");
+  ObjectWriteOperation write2;
+  write2.cmpext(0, bl, nullptr);
+  write2.write(0, new_bl);
+  ASSERT_EQ(0, ioctx.operate("foo", &write2));
+
+  ObjectReadOperation read;
+  read.read(0, bl.length(), NULL, NULL);
+  ASSERT_EQ(0, ioctx.operate("foo", &read, &bl));
+  ASSERT_EQ(0, memcmp(bl.c_str(), "CEPH", 4));
+}
+
+TEST_F(LibRadosIoPP, CmpExtDNEPP) {
+  bufferlist bl;
+  bl.append(std::string(4, '\0'));
+
+  bufferlist new_bl;
+  new_bl.append("CEPH");
+  ObjectWriteOperation write;
+  write.cmpext(0, bl, nullptr);
+  write.write(0, new_bl);
+  ASSERT_EQ(0, ioctx.operate("foo", &write));
+
+  ObjectReadOperation read;
+  read.read(0, bl.length(), NULL, NULL);
+  ASSERT_EQ(0, ioctx.operate("foo", &read, &bl));
+  ASSERT_EQ(0, memcmp(bl.c_str(), "CEPH", 4));
+}
+
+TEST_F(LibRadosIoPP, CmpExtMismatchPP) {
+  bufferlist bl;
+  bl.append("ceph");
+  ObjectWriteOperation write1;
+  write1.write(0, bl);
+  ASSERT_EQ(0, ioctx.operate("foo", &write1));
+
+  bufferlist new_bl;
+  new_bl.append("CEPH");
+  ObjectWriteOperation write2;
+  write2.cmpext(0, new_bl, nullptr);
+  write2.write(0, new_bl);
+  ASSERT_EQ(-MAX_ERRNO, ioctx.operate("foo", &write2));
+
+  ObjectReadOperation read;
+  read.read(0, bl.length(), NULL, NULL);
+  ASSERT_EQ(0, ioctx.operate("foo", &read, &bl));
+  ASSERT_EQ(0, memcmp(bl.c_str(), "ceph", 4));
+}
+
+TEST_F(LibRadosIoECPP, CmpExtPP) {
+  bufferlist bl;
+  bl.append("ceph");
+  ObjectWriteOperation write1;
+  write1.write(0, bl);
+  ASSERT_EQ(0, ioctx.operate("foo", &write1));
+
+  bufferlist new_bl;
+  new_bl.append("CEPH");
+  ObjectWriteOperation write2;
+  write2.cmpext(0, bl, nullptr);
+  write2.write_full(new_bl);
+  ASSERT_EQ(0, ioctx.operate("foo", &write2));
+
+  ObjectReadOperation read;
+  read.read(0, bl.length(), NULL, NULL);
+  ASSERT_EQ(0, ioctx.operate("foo", &read, &bl));
+  ASSERT_EQ(0, memcmp(bl.c_str(), "CEPH", 4));
+}
+
+TEST_F(LibRadosIoECPP, CmpExtDNEPP) {
+  bufferlist bl;
+  bl.append(std::string(4, '\0'));
+
+  bufferlist new_bl;
+  new_bl.append("CEPH");
+  ObjectWriteOperation write;
+  write.cmpext(0, bl, nullptr);
+  write.write_full(new_bl);
+  ASSERT_EQ(0, ioctx.operate("foo", &write));
+
+  ObjectReadOperation read;
+  read.read(0, bl.length(), NULL, NULL);
+  ASSERT_EQ(0, ioctx.operate("foo", &read, &bl));
+  ASSERT_EQ(0, memcmp(bl.c_str(), "CEPH", 4));
+}
+
+TEST_F(LibRadosIoECPP, CmpExtMismatchPP) {
+  bufferlist bl;
+  bl.append("ceph");
+  ObjectWriteOperation write1;
+  write1.write(0, bl);
+  ASSERT_EQ(0, ioctx.operate("foo", &write1));
+
+  bufferlist new_bl;
+  new_bl.append("CEPH");
+  ObjectWriteOperation write2;
+  write2.cmpext(0, new_bl, nullptr);
+  write2.write_full(new_bl);
+  ASSERT_EQ(-MAX_ERRNO, ioctx.operate("foo", &write2));
+
+  ObjectReadOperation read;
+  read.read(0, bl.length(), NULL, NULL);
+  ASSERT_EQ(0, ioctx.operate("foo", &read, &bl));
+  ASSERT_EQ(0, memcmp(bl.c_str(), "ceph", 4));
+}
index 045cd37f35b7a01c886b1463715086ed9a94601b..a1bda0aaff4835446aa3dfa97320d6613842be03 100644 (file)
@@ -18,9 +18,7 @@
 using namespace librados;
 
 typedef RadosTestNSCleanup LibRadosList;
-typedef RadosTestPPNSCleanup LibRadosListPP;
 typedef RadosTestECNSCleanup LibRadosListEC;
-typedef RadosTestECPPNSCleanup LibRadosListECPP;
 typedef RadosTestNP LibRadosListNP;
 
 
@@ -40,104 +38,6 @@ TEST_F(LibRadosList, ListObjects) {
   rados_nobjects_list_close(ctx);
 }
 
-TEST_F(LibRadosListPP, ListObjectsPP) {
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
-  NObjectIterator iter(ioctx.nobjects_begin());
-  bool foundit = false;
-  while (iter != ioctx.nobjects_end()) {
-    foundit = true;
-    ASSERT_EQ((*iter).get_oid(), "foo");
-    ++iter;
-  }
-  ASSERT_TRUE(foundit);
-}
-
-TEST_F(LibRadosListPP, ListObjectsTwicePP) {
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
-  NObjectIterator iter(ioctx.nobjects_begin());
-  bool foundit = false;
-  while (iter != ioctx.nobjects_end()) {
-    foundit = true;
-    ASSERT_EQ((*iter).get_oid(), "foo");
-    ++iter;
-  }
-  ASSERT_TRUE(foundit);
-  ++iter;
-  ASSERT_TRUE(iter == ioctx.nobjects_end());
-  foundit = false;
-  iter.seek(0);
-  while (iter != ioctx.nobjects_end()) {
-    foundit = true;
-    ASSERT_EQ((*iter).get_oid(), "foo");
-    ++iter;
-  }
-  ASSERT_TRUE(foundit);
-}
-
-TEST_F(LibRadosListPP, ListObjectsCopyIterPP) {
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
-
-  // make sure this is still valid after the original iterators are gone
-  NObjectIterator iter3;
-  {
-    NObjectIterator iter(ioctx.nobjects_begin());
-    NObjectIterator iter2(iter);
-    iter3 = iter2;
-    ASSERT_EQ((*iter).get_oid(), "foo");
-    ++iter;
-    ASSERT_TRUE(iter == ioctx.nobjects_end());
-    ++iter;
-    ASSERT_TRUE(iter == ioctx.nobjects_end());
-
-    ASSERT_EQ(iter2->get_oid(), "foo");
-    ASSERT_EQ(iter3->get_oid(), "foo");
-    ++iter2;
-    ASSERT_TRUE(iter2 == ioctx.nobjects_end());
-  }
-
-  ASSERT_EQ(iter3->get_oid(), "foo");
-  iter3 = iter3;
-  ASSERT_EQ(iter3->get_oid(), "foo");
-  ++iter3;
-  ASSERT_TRUE(iter3 == ioctx.nobjects_end());
-}
-
-TEST_F(LibRadosListPP, ListObjectsEndIter) {
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
-
-  NObjectIterator iter(ioctx.nobjects_begin());
-  NObjectIterator iter_end(ioctx.nobjects_end());
-  NObjectIterator iter_end2 = ioctx.nobjects_end();
-  ASSERT_TRUE(iter_end == iter_end2);
-  ASSERT_TRUE(iter_end == ioctx.nobjects_end());
-  ASSERT_TRUE(iter_end2 == ioctx.nobjects_end());
-
-  ASSERT_EQ(iter->get_oid(), "foo");
-  ++iter;
-  ASSERT_TRUE(iter == ioctx.nobjects_end());
-  ASSERT_TRUE(iter == iter_end);
-  ASSERT_TRUE(iter == iter_end2);
-  NObjectIterator iter2 = iter;
-  ASSERT_TRUE(iter2 == ioctx.nobjects_end());
-  ASSERT_TRUE(iter2 == iter_end);
-  ASSERT_TRUE(iter2 == iter_end2);
-}
 
 static void check_list(
   std::set<std::string>& myset,
@@ -233,108 +133,6 @@ TEST_F(LibRadosList, ListObjectsNS) {
   rados_nobjects_list_close(ctx);
 }
 
-static void check_listpp(std::set<std::string>& myset, IoCtx& ioctx, const std::string &check_nspace)
-{
-  NObjectIterator iter(ioctx.nobjects_begin());
-  std::set<std::string> orig_set(myset);
-  /**
-   * During splitting, we might see duplicate items.
-   * We assert that every object returned is in myset and that
-   * we don't hit ENOENT until we have hit every item in myset
-   * at least once.
-   */
-  while (iter != ioctx.nobjects_end()) {
-    std::string test_name;
-    if (check_nspace == all_nspaces) {
-      test_name = iter->get_nspace() + ":" + iter->get_oid();
-    } else {
-      ASSERT_TRUE(iter->get_nspace() == check_nspace);
-      test_name = iter->get_oid();
-    }
-    ASSERT_TRUE(orig_set.end() != orig_set.find(test_name));
-    myset.erase(test_name);
-    ++iter;
-  }
-  ASSERT_TRUE(myset.empty());
-}
-
-TEST_F(LibRadosListPP, ListObjectsPPNS) {
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  // Create :foo1, :foo2, :foo3, n1:foo1, ns1:foo4, ns1:foo5, ns2:foo6, n2:foo7
-  ioctx.set_namespace("");
-  ASSERT_EQ(0, ioctx.write("foo1", bl1, sizeof(buf), 0));
-  ioctx.set_namespace("ns1");
-  ASSERT_EQ(0, ioctx.write("foo1", bl1, sizeof(buf), 0));
-  ioctx.set_namespace("");
-  ASSERT_EQ(0, ioctx.write("foo2", bl1, sizeof(buf), 0));
-  ASSERT_EQ(0, ioctx.write("foo3", bl1, sizeof(buf), 0));
-  ioctx.set_namespace("ns1");
-  ASSERT_EQ(0, ioctx.write("foo4", bl1, sizeof(buf), 0));
-  ASSERT_EQ(0, ioctx.write("foo5", bl1, sizeof(buf), 0));
-  ioctx.set_namespace("ns2");
-  ASSERT_EQ(0, ioctx.write("foo6", bl1, sizeof(buf), 0));
-  ASSERT_EQ(0, ioctx.write("foo7", bl1, sizeof(buf), 0));
-  ASSERT_EQ(std::string("ns2"), ioctx.get_namespace());
-
-  std::set<std::string> def, ns1, ns2, all;
-  def.insert(std::string("foo1"));
-  def.insert(std::string("foo2"));
-  def.insert(std::string("foo3"));
-  ns1.insert(std::string("foo1"));
-  ns1.insert(std::string("foo4"));
-  ns1.insert(std::string("foo5"));
-  ns2.insert(std::string("foo6"));
-  ns2.insert(std::string("foo7"));
-  all.insert(std::string(":foo1"));
-  all.insert(std::string(":foo2"));
-  all.insert(std::string(":foo3"));
-  all.insert(std::string("ns1:foo1"));
-  all.insert(std::string("ns1:foo4"));
-  all.insert(std::string("ns1:foo5"));
-  all.insert(std::string("ns2:foo6"));
-  all.insert(std::string("ns2:foo7"));
-
-  ioctx.set_namespace("");
-  check_listpp(def, ioctx, "");
-
-  ioctx.set_namespace("ns1");
-  check_listpp(ns1, ioctx, "ns1");
-
-  ioctx.set_namespace("ns2");
-  check_listpp(ns2, ioctx, "ns2");
-
-  ioctx.set_namespace(all_nspaces);
-  check_listpp(all, ioctx, all_nspaces);
-}
-
-TEST_F(LibRadosListPP, ListObjectsManyPP) {
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl;
-  bl.append(buf, sizeof(buf));
-
-  for (int i=0; i<256; ++i) {
-    ASSERT_EQ(0, ioctx.write(stringify(i), bl, bl.length(), 0));
-  }
-
-  librados::NObjectIterator it = ioctx.nobjects_begin();
-  std::set<std::string> saw_obj;
-  std::set<int> saw_pg;
-  for (; it != ioctx.nobjects_end(); ++it) {
-    std::cout << it->get_oid()
-             << " " << it.get_pg_hash_position() << std::endl;
-    saw_obj.insert(it->get_oid());
-    saw_pg.insert(it.get_pg_hash_position());
-  }
-  std::cout << "saw " << saw_pg.size() << " pgs " << std::endl;
-
-  // make sure they are 0..n
-  for (unsigned i = 0; i < saw_pg.size(); ++i)
-    ASSERT_TRUE(saw_pg.count(i));
-}
 
 TEST_F(LibRadosList, ListObjectsStart) {
   char buf[128];
@@ -369,175 +167,17 @@ TEST_F(LibRadosList, ListObjectsStart) {
   rados_nobjects_list_close(ctx);
 }
 
-TEST_F(LibRadosListPP, ListObjectsStartPP) {
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl;
-  bl.append(buf, sizeof(buf));
-
-  for (int i=0; i<16; ++i) {
-    ASSERT_EQ(0, ioctx.write(stringify(i), bl, bl.length(), 0));
-  }
-
-  librados::NObjectIterator it = ioctx.nobjects_begin();
-  std::map<int, std::set<std::string> > pg_to_obj;
-  for (; it != ioctx.nobjects_end(); ++it) {
-    std::cout << it->get_oid() << " " << it.get_pg_hash_position() << std::endl;
-    pg_to_obj[it.get_pg_hash_position()].insert(it->get_oid());
-  }
-
-  std::map<int, std::set<std::string> >::reverse_iterator p =
-    pg_to_obj.rbegin();
-  it = ioctx.nobjects_begin(p->first);
-  while (p != pg_to_obj.rend()) {
-    ASSERT_EQ((uint32_t)p->first, it.seek(p->first));
-    std::cout << "have " << it->get_oid() << " expect one of " << p->second << std::endl;
-    ASSERT_TRUE(p->second.count(it->get_oid()));
-    ++p;
-  }
-}
-
-TEST_F(LibRadosListPP, ListObjectsCursorNSPP) {
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl;
-  bl.append(buf, sizeof(buf));
-
-  const int max_objs = 16;
-
-  map<string, string> oid_to_ns;
-
-  for (int i=0; i<max_objs; ++i) {
-    stringstream ss;
-    ss << "ns" << i / 4;
-    ioctx.set_namespace(ss.str());
-    string oid = stringify(i);
-    ASSERT_EQ(0, ioctx.write(oid, bl, bl.length(), 0));
-
-    oid_to_ns[oid] = ss.str();
-  }
-
-  ioctx.set_namespace(all_nspaces);
-
-  librados::NObjectIterator it = ioctx.nobjects_begin();
-  std::map<librados::ObjectCursor, string> cursor_to_obj;
-
-  int count = 0;
-
-  librados::ObjectCursor seek_cursor;
-
-  map<string, list<librados::ObjectCursor> > ns_to_cursors;
-
-  for (it = ioctx.nobjects_begin(); it != ioctx.nobjects_end(); ++it) {
-    librados::ObjectCursor cursor = it.get_cursor();
-    string oid = it->get_oid();
-    cout << "> oid=" << oid << " cursor=" << it.get_cursor() << std::endl;
-  }
-
-  vector<string> objs_order;
-
-  for (it = ioctx.nobjects_begin(); it != ioctx.nobjects_end(); ++it, ++count) {
-    librados::ObjectCursor cursor = it.get_cursor();
-    string oid = it->get_oid();
-    std::cout << oid << " " << it.get_pg_hash_position() << std::endl;
-    cout << ": oid=" << oid << " cursor=" << it.get_cursor() << std::endl;
-    cursor_to_obj[cursor] = oid;
-
-    ASSERT_EQ(oid_to_ns[oid], it->get_nspace());
-
-    it.seek(cursor);
-    cout << ": seek to " << cursor << " it.cursor=" << it.get_cursor() << std::endl;
-    ASSERT_EQ(oid, it->get_oid());
-    ASSERT_LT(count, max_objs); /* avoid infinite loops due to bad seek */
-
-    ns_to_cursors[it->get_nspace()].push_back(cursor);
-
-    if (count == max_objs/2) {
-      seek_cursor = cursor;
-    }
-    objs_order.push_back(it->get_oid());
-  }
-
-  ASSERT_EQ(count, max_objs);
-
-  /* check that reading past seek also works */
-  cout << "seek_cursor=" << seek_cursor << std::endl;
-  it.seek(seek_cursor);
-  for (count = max_objs/2; count < max_objs; ++count, ++it) {
-    ASSERT_EQ(objs_order[count], it->get_oid());
-  }
-
-  /* seek to all cursors, check that we get expected obj */
-  for (auto& niter : ns_to_cursors) {
-    const string& ns = niter.first;
-    list<librados::ObjectCursor>& cursors = niter.second;
-
-    for (auto& cursor : cursors) {
-      cout << ": seek to " << cursor << std::endl;
-      it.seek(cursor);
-      ASSERT_EQ(cursor, it.get_cursor());
-      string& expected_oid = cursor_to_obj[cursor];
-      cout << ": it->get_cursor()=" << it.get_cursor() << " expected=" << cursor << std::endl;
-      cout << ": it->get_oid()=" << it->get_oid() << " expected=" << expected_oid << std::endl;
-      cout << ": it->get_nspace()=" << it->get_oid() << " expected=" << ns << std::endl;
-      ASSERT_EQ(expected_oid, it->get_oid());
-      ASSERT_EQ(it->get_nspace(), ns);
-    }
-  }
-}
-
-TEST_F(LibRadosListPP, ListObjectsCursorPP) {
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl;
-  bl.append(buf, sizeof(buf));
-
-  const int max_objs = 16;
-
-  for (int i=0; i<max_objs; ++i) {
-    stringstream ss;
-    ss << "ns" << i / 4;
-    ioctx.set_namespace(ss.str());
-    ASSERT_EQ(0, ioctx.write(stringify(i), bl, bl.length(), 0));
-  }
-
-  ioctx.set_namespace(all_nspaces);
-
-  librados::NObjectIterator it = ioctx.nobjects_begin();
-  std::map<librados::ObjectCursor, string> cursor_to_obj;
-
-  int count = 0;
-
-  for (; it != ioctx.nobjects_end(); ++it, ++count) {
-    librados::ObjectCursor cursor = it.get_cursor();
-    string oid = it->get_oid();
-    std::cout << oid << " " << it.get_pg_hash_position() << std::endl;
-    cout << ": oid=" << oid << " cursor=" << it.get_cursor() << std::endl;
-    cursor_to_obj[cursor] = oid;
-
-    it.seek(cursor);
-    cout << ": seek to " << cursor << std::endl;
-    ASSERT_EQ(oid, it->get_oid());
-    ASSERT_LT(count, max_objs); /* avoid infinite loops due to bad seek */
-  }
-
-  ASSERT_EQ(count, max_objs);
-
-  auto p = cursor_to_obj.rbegin();
-  it = ioctx.nobjects_begin();
-  while (p != cursor_to_obj.rend()) {
-    cout << ": seek to " << p->first << std::endl;
-    it.seek(p->first);
-    ASSERT_EQ(p->first, it.get_cursor());
-    cout << ": it->get_cursor()=" << it.get_cursor() << " expected=" << p->first << std::endl;
-    cout << ": it->get_oid()=" << it->get_oid() << " expected=" << p->second << std::endl;
-    ASSERT_EQ(p->second, it->get_oid());
-
-    librados::NObjectIterator it2 = ioctx.nobjects_begin(it.get_cursor());
-    ASSERT_EQ(it2->get_oid(), it->get_oid());
-
-    ++p;
+// this function replicates
+// librados::operator<<(std::ostream& os, const librados::ObjectCursor& oc)
+// because we don't want to use librados-cxx in librados client.
+std::ostream& operator<<(std::ostream&os, const rados_object_list_cursor& oc)
+{
+  if (oc) {
+    os << *(hobject_t *)oc;
+  } else {
+    os << hobject_t{};
   }
+  return os;
 }
 
 TEST_F(LibRadosList, ListObjectsCursor) {
@@ -558,15 +198,15 @@ TEST_F(LibRadosList, ListObjectsCursor) {
     ASSERT_EQ(0, rados_nobjects_list_open(ioctx, &ctx));
     ASSERT_EQ(rados_nobjects_list_get_cursor(ctx, &cursor), 0);
     rados_object_list_cursor first_cursor = cursor;
-    cout << "x cursor=" << ObjectCursor(cursor) << std::endl;
+    cout << "x cursor=" << cursor << std::endl;
     while (rados_nobjects_list_next(ctx, &entry, NULL, NULL) == 0) {
       string oid = entry;
       ASSERT_EQ(rados_nobjects_list_get_cursor(ctx, &cursor), 0);
-      cout << "> oid=" << oid << " cursor=" << ObjectCursor(cursor) << std::endl;
+      cout << "> oid=" << oid << " cursor=" << cursor << std::endl;
     }
     rados_nobjects_list_seek_cursor(ctx, first_cursor);
     ASSERT_EQ(rados_nobjects_list_next(ctx, &entry, NULL, NULL), 0);
-    cout << "FIRST> seek to " << ObjectCursor(first_cursor) << " oid=" << string(entry) << std::endl;
+    cout << "FIRST> seek to " << first_cursor << " oid=" << string(entry) << std::endl;
   }
   rados_list_ctx_t ctx;
   ASSERT_EQ(0, rados_nobjects_list_open(ioctx, &ctx));
@@ -579,13 +219,13 @@ TEST_F(LibRadosList, ListObjectsCursor) {
     rados_object_list_cursor cursor;
     ASSERT_EQ(rados_nobjects_list_get_cursor(ctx, &cursor), 0);
     string oid = entry;
-    cout << ": oid=" << oid << " cursor=" << ObjectCursor(cursor) << std::endl;
+    cout << ": oid=" << oid << " cursor=" << cursor << std::endl;
     cursor_to_obj[cursor] = oid;
 
     rados_nobjects_list_seek_cursor(ctx, cursor);
-    cout << ": seek to " << ObjectCursor(cursor) << std::endl;
+    cout << ": seek to " << cursor << std::endl;
     ASSERT_EQ(rados_nobjects_list_next(ctx, &entry, NULL, NULL), 0);
-    cout << "> " << ObjectCursor(cursor) << " -> " << entry << std::endl;
+    cout << "> " << cursor << " -> " << entry << std::endl;
     ASSERT_EQ(string(entry), oid);
     ASSERT_LT(count, max_objs); /* avoid infinite loops due to bad seek */
 
@@ -597,15 +237,15 @@ TEST_F(LibRadosList, ListObjectsCursor) {
   auto p = cursor_to_obj.rbegin();
   ASSERT_EQ(0, rados_nobjects_list_open(ioctx, &ctx));
   while (p != cursor_to_obj.rend()) {
-    cout << ": seek to " << ObjectCursor(p->first) << std::endl;
+    cout << ": seek to " << p->first << std::endl;
     rados_object_list_cursor cursor;
     rados_object_list_cursor oid(p->first);
     rados_nobjects_list_seek_cursor(ctx, oid);
     ASSERT_EQ(rados_nobjects_list_get_cursor(ctx, &cursor), 0);
-    cout << ": cursor()=" << ObjectCursor(cursor) << " expected=" << oid << std::endl;
+    cout << ": cursor()=" << cursor << " expected=" << oid << std::endl;
     // ASSERT_EQ(ObjectCursor(oid), ObjectCursor(cursor));
     ASSERT_EQ(rados_nobjects_list_next(ctx, &entry, NULL, NULL), 0);
-    cout << "> " << ObjectCursor(cursor) << " -> " << entry << std::endl;
+    cout << "> " << cursor << " -> " << entry << std::endl;
     cout << ": entry=" << entry << " expected=" << p->second << std::endl;
     ASSERT_EQ(p->second, string(entry));
 
@@ -631,105 +271,6 @@ TEST_F(LibRadosListEC, ListObjects) {
   rados_nobjects_list_close(ctx);
 }
 
-TEST_F(LibRadosListECPP, ListObjectsPP) {
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
-  NObjectIterator iter(ioctx.nobjects_begin());
-  bool foundit = false;
-  while (iter != ioctx.nobjects_end()) {
-    foundit = true;
-    ASSERT_EQ((*iter).get_oid(), "foo");
-    ++iter;
-  }
-  ASSERT_TRUE(foundit);
-}
-
-TEST_F(LibRadosListECPP, ListObjectsTwicePP) {
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
-  NObjectIterator iter(ioctx.nobjects_begin());
-  bool foundit = false;
-  while (iter != ioctx.nobjects_end()) {
-    foundit = true;
-    ASSERT_EQ((*iter).get_oid(), "foo");
-    ++iter;
-  }
-  ASSERT_TRUE(foundit);
-  ++iter;
-  ASSERT_TRUE(iter == ioctx.nobjects_end());
-  foundit = false;
-  iter.seek(0);
-  while (iter != ioctx.nobjects_end()) {
-    foundit = true;
-    ASSERT_EQ((*iter).get_oid(), "foo");
-    ++iter;
-  }
-  ASSERT_TRUE(foundit);
-}
-
-TEST_F(LibRadosListECPP, ListObjectsCopyIterPP) {
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
-
-  // make sure this is still valid after the original iterators are gone
-  NObjectIterator iter3;
-  {
-    NObjectIterator iter(ioctx.nobjects_begin());
-    NObjectIterator iter2(iter);
-    iter3 = iter2;
-    ASSERT_EQ((*iter).get_oid(), "foo");
-    ++iter;
-    ASSERT_TRUE(iter == ioctx.nobjects_end());
-    ++iter;
-    ASSERT_TRUE(iter == ioctx.nobjects_end());
-
-    ASSERT_EQ(iter2->get_oid(), "foo");
-    ASSERT_EQ(iter3->get_oid(), "foo");
-    ++iter2;
-    ASSERT_TRUE(iter2 == ioctx.nobjects_end());
-  }
-
-  ASSERT_EQ(iter3->get_oid(), "foo");
-  iter3 = iter3;
-  ASSERT_EQ(iter3->get_oid(), "foo");
-  ++iter3;
-  ASSERT_TRUE(iter3 == ioctx.nobjects_end());
-}
-
-TEST_F(LibRadosListECPP, ListObjectsEndIter) {
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
-
-  NObjectIterator iter(ioctx.nobjects_begin());
-  NObjectIterator iter_end(ioctx.nobjects_end());
-  NObjectIterator iter_end2 = ioctx.nobjects_end();
-  ASSERT_TRUE(iter_end == iter_end2);
-  ASSERT_TRUE(iter_end == ioctx.nobjects_end());
-  ASSERT_TRUE(iter_end2 == ioctx.nobjects_end());
-
-  ASSERT_EQ(iter->get_oid(), "foo");
-  ++iter;
-  ASSERT_TRUE(iter == ioctx.nobjects_end());
-  ASSERT_TRUE(iter == iter_end);
-  ASSERT_TRUE(iter == iter_end2);
-  NObjectIterator iter2 = iter;
-  ASSERT_TRUE(iter2 == ioctx.nobjects_end());
-  ASSERT_TRUE(iter2 == iter_end);
-  ASSERT_TRUE(iter2 == iter_end2);
-}
-
 TEST_F(LibRadosListEC, ListObjectsNS) {
   char buf[128];
   memset(buf, 0xcc, sizeof(buf));
@@ -792,71 +333,6 @@ TEST_F(LibRadosListEC, ListObjectsNS) {
   rados_nobjects_list_close(ctx);
 }
 
-TEST_F(LibRadosListECPP, ListObjectsPPNS) {
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  // Create :foo1, :foo2, :foo3, n1:foo1, ns1:foo4, ns1:foo5, ns2:foo6, n2:foo7
-  ioctx.set_namespace("");
-  ASSERT_EQ(0, ioctx.write("foo1", bl1, sizeof(buf), 0));
-  ioctx.set_namespace("ns1");
-  ASSERT_EQ(0, ioctx.write("foo1", bl1, sizeof(buf), 0));
-  ioctx.set_namespace("");
-  ASSERT_EQ(0, ioctx.write("foo2", bl1, sizeof(buf), 0));
-  ASSERT_EQ(0, ioctx.write("foo3", bl1, sizeof(buf), 0));
-  ioctx.set_namespace("ns1");
-  ASSERT_EQ(0, ioctx.write("foo4", bl1, sizeof(buf), 0));
-  ASSERT_EQ(0, ioctx.write("foo5", bl1, sizeof(buf), 0));
-  ioctx.set_namespace("ns2");
-  ASSERT_EQ(0, ioctx.write("foo6", bl1, sizeof(buf), 0));
-  ASSERT_EQ(0, ioctx.write("foo7", bl1, sizeof(buf), 0));
-
-  std::set<std::string> def, ns1, ns2;
-  def.insert(std::string("foo1"));
-  def.insert(std::string("foo2"));
-  def.insert(std::string("foo3"));
-  ns1.insert(std::string("foo1"));
-  ns1.insert(std::string("foo4"));
-  ns1.insert(std::string("foo5"));
-  ns2.insert(std::string("foo6"));
-  ns2.insert(std::string("foo7"));
-
-  ioctx.set_namespace("");
-  check_listpp(def, ioctx, "");
-
-  ioctx.set_namespace("ns1");
-  check_listpp(ns1, ioctx, "ns1");
-
-  ioctx.set_namespace("ns2");
-  check_listpp(ns2, ioctx, "ns2");
-}
-
-TEST_F(LibRadosListECPP, ListObjectsManyPP) {
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl;
-  bl.append(buf, sizeof(buf));
-
-  for (int i=0; i<256; ++i) {
-    ASSERT_EQ(0, ioctx.write(stringify(i), bl, bl.length(), 0));
-  }
-
-  librados::NObjectIterator it = ioctx.nobjects_begin();
-  std::set<std::string> saw_obj;
-  std::set<int> saw_pg;
-  for (; it != ioctx.nobjects_end(); ++it) {
-    std::cout << it->get_oid()
-             << " " << it.get_pg_hash_position() << std::endl;
-    saw_obj.insert(it->get_oid());
-    saw_pg.insert(it.get_pg_hash_position());
-  }
-  std::cout << "saw " << saw_pg.size() << " pgs " << std::endl;
-
-  // make sure they are 0..n
-  for (unsigned i = 0; i < saw_pg.size(); ++i)
-    ASSERT_TRUE(saw_pg.count(i));
-}
 
 TEST_F(LibRadosListEC, ListObjectsStart) {
   char buf[128];
@@ -891,76 +367,6 @@ TEST_F(LibRadosListEC, ListObjectsStart) {
   rados_nobjects_list_close(ctx);
 }
 
-TEST_F(LibRadosListECPP, ListObjectsStartPP) {
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl;
-  bl.append(buf, sizeof(buf));
-
-  for (int i=0; i<16; ++i) {
-    ASSERT_EQ(0, ioctx.write(stringify(i), bl, bl.length(), 0));
-  }
-
-  librados::NObjectIterator it = ioctx.nobjects_begin();
-  std::map<int, std::set<std::string> > pg_to_obj;
-  for (; it != ioctx.nobjects_end(); ++it) {
-    std::cout << it->get_oid() << " " << it.get_pg_hash_position() << std::endl;
-    pg_to_obj[it.get_pg_hash_position()].insert(it->get_oid());
-  }
-
-  std::map<int, std::set<std::string> >::reverse_iterator p =
-    pg_to_obj.rbegin();
-  it = ioctx.nobjects_begin(p->first);
-  while (p != pg_to_obj.rend()) {
-    ASSERT_EQ((uint32_t)p->first, it.seek(p->first));
-    std::cout << "have " << it->get_oid() << " expect one of " << p->second << std::endl;
-    ASSERT_TRUE(p->second.count(it->get_oid()));
-    ++p;
-  }
-}
-
-TEST_F(LibRadosListPP, ListObjectsFilterPP) {
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist obj_content;
-  obj_content.append(buf, sizeof(buf));
-
-  std::string target_str = "content";
-
-  // Write xattr bare, no ::encod'ing
-  bufferlist target_val;
-  target_val.append(target_str);
-  bufferlist nontarget_val;
-  nontarget_val.append("rhubarb");
-
-  ASSERT_EQ(0, ioctx.write("has_xattr", obj_content, obj_content.length(), 0));
-  ASSERT_EQ(0, ioctx.write("has_wrong_xattr", obj_content, obj_content.length(), 0));
-  ASSERT_EQ(0, ioctx.write("no_xattr", obj_content, obj_content.length(), 0));
-
-  ASSERT_EQ(0, ioctx.setxattr("has_xattr", "theattr", target_val));
-  ASSERT_EQ(0, ioctx.setxattr("has_wrong_xattr", "theattr", nontarget_val));
-
-  bufferlist filter_bl;
-  std::string filter_name = "plain";
-  encode(filter_name, filter_bl);
-  encode("_theattr", filter_bl);
-  encode(target_str, filter_bl);
-
-  NObjectIterator iter(ioctx.nobjects_begin(filter_bl));
-  bool foundit = false;
-  int k = 0;
-  while (iter != ioctx.nobjects_end()) {
-    foundit = true;
-    // We should only see the object that matches the filter
-    ASSERT_EQ((*iter).get_oid(), "has_xattr");
-    // We should only see it once
-    ASSERT_EQ(k, 0);
-    ++iter;
-    ++k;
-  }
-  ASSERT_TRUE(foundit);
-}
-
 TEST_F(LibRadosListNP, ListObjectsError) {
   std::string pool_name;
   rados_t cluster;
@@ -1117,147 +523,3 @@ TEST_F(LibRadosList, EnumerateObjectsSplit) {
   }
   ASSERT_EQ(n_objects, saw_obj.size());
 }
-
-TEST_F(LibRadosListPP, EnumerateObjectsPP) {
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl;
-  bl.append(buf, sizeof(buf));
-
-  const uint32_t n_objects = 16;
-  for (unsigned i=0; i<n_objects; ++i) {
-    ASSERT_EQ(0, ioctx.write(stringify(i), bl, sizeof(buf), 0));
-  }
-
-  std::set<std::string> saw_obj;
-  ObjectCursor c = ioctx.object_list_begin();
-  ObjectCursor end = ioctx.object_list_end();
-  while(!ioctx.object_list_is_end(c))
-  {
-    std::vector<ObjectItem> result;
-    int r = ioctx.object_list(c, end, 12, {}, &result, &c);
-    ASSERT_GE(r, 0);
-    ASSERT_EQ(r, (int)result.size());
-    for (int i = 0; i < r; ++i) {
-      auto oid = result[i].oid;
-      if (saw_obj.count(oid)) {
-          std::cerr << "duplicate obj " << oid << std::endl;
-      }
-      ASSERT_FALSE(saw_obj.count(oid));
-      saw_obj.insert(oid);
-    }
-  }
-
-  for (unsigned i=0; i<n_objects; ++i) {
-    if (!saw_obj.count(stringify(i))) {
-        std::cerr << "missing object " << i << std::endl;
-    }
-    ASSERT_TRUE(saw_obj.count(stringify(i)));
-  }
-  ASSERT_EQ(n_objects, saw_obj.size());
-}
-
-TEST_F(LibRadosListPP, EnumerateObjectsSplitPP) {
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl;
-  bl.append(buf, sizeof(buf));
-
-  const uint32_t n_objects = 16;
-  for (unsigned i=0; i<n_objects; ++i) {
-    ASSERT_EQ(0, ioctx.write(stringify(i), bl, sizeof(buf), 0));
-  }
-
-  ObjectCursor begin = ioctx.object_list_begin();
-  ObjectCursor end = ioctx.object_list_end();
-
-  // Step through an odd number of shards
-  unsigned m = 5;
-  std::set<std::string> saw_obj;
-  for (unsigned n = 0; n < m; ++n) {
-      ObjectCursor shard_start;
-      ObjectCursor shard_end;
-
-      ioctx.object_list_slice(
-        begin,
-        end,
-        n,
-        m,
-        &shard_start,
-        &shard_end);
-
-      ObjectCursor c(shard_start);
-      while(c < shard_end)
-      {
-        std::vector<ObjectItem> result;
-        int r = ioctx.object_list(c, shard_end, 12, {}, &result, &c);
-        ASSERT_GE(r, 0);
-
-        for (const auto & i : result) {
-          const auto &oid = i.oid;
-          if (saw_obj.count(oid)) {
-              std::cerr << "duplicate obj " << oid << std::endl;
-          }
-          ASSERT_FALSE(saw_obj.count(oid));
-          saw_obj.insert(oid);
-        }
-      }
-  }
-
-  for (unsigned i=0; i<n_objects; ++i) {
-    if (!saw_obj.count(stringify(i))) {
-        std::cerr << "missing object " << i << std::endl;
-    }
-    ASSERT_TRUE(saw_obj.count(stringify(i)));
-  }
-  ASSERT_EQ(n_objects, saw_obj.size());
-}
-
-
-TEST_F(LibRadosListPP, EnumerateObjectsFilterPP) {
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist obj_content;
-  obj_content.append(buf, sizeof(buf));
-
-  std::string target_str = "content";
-
-  // Write xattr bare, no ::encod'ing
-  bufferlist target_val;
-  target_val.append(target_str);
-  bufferlist nontarget_val;
-  nontarget_val.append("rhubarb");
-
-  ASSERT_EQ(0, ioctx.write("has_xattr", obj_content, obj_content.length(), 0));
-  ASSERT_EQ(0, ioctx.write("has_wrong_xattr", obj_content, obj_content.length(), 0));
-  ASSERT_EQ(0, ioctx.write("no_xattr", obj_content, obj_content.length(), 0));
-
-  ASSERT_EQ(0, ioctx.setxattr("has_xattr", "theattr", target_val));
-  ASSERT_EQ(0, ioctx.setxattr("has_wrong_xattr", "theattr", nontarget_val));
-
-  bufferlist filter_bl;
-  std::string filter_name = "plain";
-  encode(filter_name, filter_bl);
-  encode("_theattr", filter_bl);
-  encode(target_str, filter_bl);
-
-  ObjectCursor c = ioctx.object_list_begin();
-  ObjectCursor end = ioctx.object_list_end();
-  bool foundit = false;
-  while(!ioctx.object_list_is_end(c))
-  {
-    std::vector<ObjectItem> result;
-    int r = ioctx.object_list(c, end, 12, filter_bl, &result, &c);
-    ASSERT_GE(r, 0);
-    ASSERT_EQ(r, (int)result.size());
-    for (int i = 0; i < r; ++i) {
-      auto oid = result[i].oid;
-      // We should only see the object that matches the filter
-      ASSERT_EQ(oid, "has_xattr");
-      // We should only see it once
-      ASSERT_FALSE(foundit);
-      foundit = true;
-    }
-  }
-  ASSERT_TRUE(foundit);
-}
diff --git a/src/test/librados/list_cxx.cc b/src/test/librados/list_cxx.cc
new file mode 100644 (file)
index 0000000..45368b3
--- /dev/null
@@ -0,0 +1,773 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#include <errno.h>
+#include <string>
+#include <stdexcept>
+
+#include "gtest/gtest.h"
+
+#include "include/rados/librados.hpp"
+#include "include/stringify.h"
+#include "include/types.h"
+#include "common/hobject.h"
+#include "test/librados/test_cxx.h"
+#include "test/librados/test_common.h"
+#include "test/librados/testcase_cxx.h"
+#include "global/global_context.h"
+
+using namespace librados;
+
+typedef RadosTestPPNSCleanup LibRadosListPP;
+typedef RadosTestECPPNSCleanup LibRadosListECPP;
+
+TEST_F(LibRadosListPP, ListObjectsPP) {
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+  NObjectIterator iter(ioctx.nobjects_begin());
+  bool foundit = false;
+  while (iter != ioctx.nobjects_end()) {
+    foundit = true;
+    ASSERT_EQ((*iter).get_oid(), "foo");
+    ++iter;
+  }
+  ASSERT_TRUE(foundit);
+}
+
+TEST_F(LibRadosListPP, ListObjectsTwicePP) {
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+  NObjectIterator iter(ioctx.nobjects_begin());
+  bool foundit = false;
+  while (iter != ioctx.nobjects_end()) {
+    foundit = true;
+    ASSERT_EQ((*iter).get_oid(), "foo");
+    ++iter;
+  }
+  ASSERT_TRUE(foundit);
+  ++iter;
+  ASSERT_TRUE(iter == ioctx.nobjects_end());
+  foundit = false;
+  iter.seek(0);
+  while (iter != ioctx.nobjects_end()) {
+    foundit = true;
+    ASSERT_EQ((*iter).get_oid(), "foo");
+    ++iter;
+  }
+  ASSERT_TRUE(foundit);
+}
+
+TEST_F(LibRadosListPP, ListObjectsCopyIterPP) {
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+
+  // make sure this is still valid after the original iterators are gone
+  NObjectIterator iter3;
+  {
+    NObjectIterator iter(ioctx.nobjects_begin());
+    NObjectIterator iter2(iter);
+    iter3 = iter2;
+    ASSERT_EQ((*iter).get_oid(), "foo");
+    ++iter;
+    ASSERT_TRUE(iter == ioctx.nobjects_end());
+    ++iter;
+    ASSERT_TRUE(iter == ioctx.nobjects_end());
+
+    ASSERT_EQ(iter2->get_oid(), "foo");
+    ASSERT_EQ(iter3->get_oid(), "foo");
+    ++iter2;
+    ASSERT_TRUE(iter2 == ioctx.nobjects_end());
+  }
+
+  ASSERT_EQ(iter3->get_oid(), "foo");
+  iter3 = iter3;
+  ASSERT_EQ(iter3->get_oid(), "foo");
+  ++iter3;
+  ASSERT_TRUE(iter3 == ioctx.nobjects_end());
+}
+
+TEST_F(LibRadosListPP, ListObjectsEndIter) {
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+
+  NObjectIterator iter(ioctx.nobjects_begin());
+  NObjectIterator iter_end(ioctx.nobjects_end());
+  NObjectIterator iter_end2 = ioctx.nobjects_end();
+  ASSERT_TRUE(iter_end == iter_end2);
+  ASSERT_TRUE(iter_end == ioctx.nobjects_end());
+  ASSERT_TRUE(iter_end2 == ioctx.nobjects_end());
+
+  ASSERT_EQ(iter->get_oid(), "foo");
+  ++iter;
+  ASSERT_TRUE(iter == ioctx.nobjects_end());
+  ASSERT_TRUE(iter == iter_end);
+  ASSERT_TRUE(iter == iter_end2);
+  NObjectIterator iter2 = iter;
+  ASSERT_TRUE(iter2 == ioctx.nobjects_end());
+  ASSERT_TRUE(iter2 == iter_end);
+  ASSERT_TRUE(iter2 == iter_end2);
+}
+
+static void check_listpp(std::set<std::string>& myset, IoCtx& ioctx, const std::string &check_nspace)
+{
+  NObjectIterator iter(ioctx.nobjects_begin());
+  std::set<std::string> orig_set(myset);
+  /**
+   * During splitting, we might see duplicate items.
+   * We assert that every object returned is in myset and that
+   * we don't hit ENOENT until we have hit every item in myset
+   * at least once.
+   */
+  while (iter != ioctx.nobjects_end()) {
+    std::string test_name;
+    if (check_nspace == all_nspaces) {
+      test_name = iter->get_nspace() + ":" + iter->get_oid();
+    } else {
+      ASSERT_TRUE(iter->get_nspace() == check_nspace);
+      test_name = iter->get_oid();
+    }
+    ASSERT_TRUE(orig_set.end() != orig_set.find(test_name));
+    myset.erase(test_name);
+    ++iter;
+  }
+  ASSERT_TRUE(myset.empty());
+}
+
+TEST_F(LibRadosListPP, ListObjectsPPNS) {
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  // Create :foo1, :foo2, :foo3, n1:foo1, ns1:foo4, ns1:foo5, ns2:foo6, n2:foo7
+  ioctx.set_namespace("");
+  ASSERT_EQ(0, ioctx.write("foo1", bl1, sizeof(buf), 0));
+  ioctx.set_namespace("ns1");
+  ASSERT_EQ(0, ioctx.write("foo1", bl1, sizeof(buf), 0));
+  ioctx.set_namespace("");
+  ASSERT_EQ(0, ioctx.write("foo2", bl1, sizeof(buf), 0));
+  ASSERT_EQ(0, ioctx.write("foo3", bl1, sizeof(buf), 0));
+  ioctx.set_namespace("ns1");
+  ASSERT_EQ(0, ioctx.write("foo4", bl1, sizeof(buf), 0));
+  ASSERT_EQ(0, ioctx.write("foo5", bl1, sizeof(buf), 0));
+  ioctx.set_namespace("ns2");
+  ASSERT_EQ(0, ioctx.write("foo6", bl1, sizeof(buf), 0));
+  ASSERT_EQ(0, ioctx.write("foo7", bl1, sizeof(buf), 0));
+  ASSERT_EQ(std::string("ns2"), ioctx.get_namespace());
+
+  std::set<std::string> def, ns1, ns2, all;
+  def.insert(std::string("foo1"));
+  def.insert(std::string("foo2"));
+  def.insert(std::string("foo3"));
+  ns1.insert(std::string("foo1"));
+  ns1.insert(std::string("foo4"));
+  ns1.insert(std::string("foo5"));
+  ns2.insert(std::string("foo6"));
+  ns2.insert(std::string("foo7"));
+  all.insert(std::string(":foo1"));
+  all.insert(std::string(":foo2"));
+  all.insert(std::string(":foo3"));
+  all.insert(std::string("ns1:foo1"));
+  all.insert(std::string("ns1:foo4"));
+  all.insert(std::string("ns1:foo5"));
+  all.insert(std::string("ns2:foo6"));
+  all.insert(std::string("ns2:foo7"));
+
+  ioctx.set_namespace("");
+  check_listpp(def, ioctx, "");
+
+  ioctx.set_namespace("ns1");
+  check_listpp(ns1, ioctx, "ns1");
+
+  ioctx.set_namespace("ns2");
+  check_listpp(ns2, ioctx, "ns2");
+
+  ioctx.set_namespace(all_nspaces);
+  check_listpp(all, ioctx, all_nspaces);
+}
+
+TEST_F(LibRadosListPP, ListObjectsManyPP) {
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl;
+  bl.append(buf, sizeof(buf));
+
+  for (int i=0; i<256; ++i) {
+    ASSERT_EQ(0, ioctx.write(stringify(i), bl, bl.length(), 0));
+  }
+
+  librados::NObjectIterator it = ioctx.nobjects_begin();
+  std::set<std::string> saw_obj;
+  std::set<int> saw_pg;
+  for (; it != ioctx.nobjects_end(); ++it) {
+    std::cout << it->get_oid()
+             << " " << it.get_pg_hash_position() << std::endl;
+    saw_obj.insert(it->get_oid());
+    saw_pg.insert(it.get_pg_hash_position());
+  }
+  std::cout << "saw " << saw_pg.size() << " pgs " << std::endl;
+
+  // make sure they are 0..n
+  for (unsigned i = 0; i < saw_pg.size(); ++i)
+    ASSERT_TRUE(saw_pg.count(i));
+}
+
+TEST_F(LibRadosListPP, ListObjectsStartPP) {
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl;
+  bl.append(buf, sizeof(buf));
+
+  for (int i=0; i<16; ++i) {
+    ASSERT_EQ(0, ioctx.write(stringify(i), bl, bl.length(), 0));
+  }
+
+  librados::NObjectIterator it = ioctx.nobjects_begin();
+  std::map<int, std::set<std::string> > pg_to_obj;
+  for (; it != ioctx.nobjects_end(); ++it) {
+    std::cout << it->get_oid() << " " << it.get_pg_hash_position() << std::endl;
+    pg_to_obj[it.get_pg_hash_position()].insert(it->get_oid());
+  }
+
+  std::map<int, std::set<std::string> >::reverse_iterator p =
+    pg_to_obj.rbegin();
+  it = ioctx.nobjects_begin(p->first);
+  while (p != pg_to_obj.rend()) {
+    ASSERT_EQ((uint32_t)p->first, it.seek(p->first));
+    std::cout << "have " << it->get_oid() << " expect one of " << p->second << std::endl;
+    ASSERT_TRUE(p->second.count(it->get_oid()));
+    ++p;
+  }
+}
+
+TEST_F(LibRadosListPP, ListObjectsCursorNSPP) {
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl;
+  bl.append(buf, sizeof(buf));
+
+  const int max_objs = 16;
+
+  map<string, string> oid_to_ns;
+
+  for (int i=0; i<max_objs; ++i) {
+    stringstream ss;
+    ss << "ns" << i / 4;
+    ioctx.set_namespace(ss.str());
+    string oid = stringify(i);
+    ASSERT_EQ(0, ioctx.write(oid, bl, bl.length(), 0));
+
+    oid_to_ns[oid] = ss.str();
+  }
+
+  ioctx.set_namespace(all_nspaces);
+
+  librados::NObjectIterator it = ioctx.nobjects_begin();
+  std::map<librados::ObjectCursor, string> cursor_to_obj;
+
+  int count = 0;
+
+  librados::ObjectCursor seek_cursor;
+
+  map<string, list<librados::ObjectCursor> > ns_to_cursors;
+
+  for (it = ioctx.nobjects_begin(); it != ioctx.nobjects_end(); ++it) {
+    librados::ObjectCursor cursor = it.get_cursor();
+    string oid = it->get_oid();
+    cout << "> oid=" << oid << " cursor=" << it.get_cursor() << std::endl;
+  }
+
+  vector<string> objs_order;
+
+  for (it = ioctx.nobjects_begin(); it != ioctx.nobjects_end(); ++it, ++count) {
+    librados::ObjectCursor cursor = it.get_cursor();
+    string oid = it->get_oid();
+    std::cout << oid << " " << it.get_pg_hash_position() << std::endl;
+    cout << ": oid=" << oid << " cursor=" << it.get_cursor() << std::endl;
+    cursor_to_obj[cursor] = oid;
+
+    ASSERT_EQ(oid_to_ns[oid], it->get_nspace());
+
+    it.seek(cursor);
+    cout << ": seek to " << cursor << " it.cursor=" << it.get_cursor() << std::endl;
+    ASSERT_EQ(oid, it->get_oid());
+    ASSERT_LT(count, max_objs); /* avoid infinite loops due to bad seek */
+
+    ns_to_cursors[it->get_nspace()].push_back(cursor);
+
+    if (count == max_objs/2) {
+      seek_cursor = cursor;
+    }
+    objs_order.push_back(it->get_oid());
+  }
+
+  ASSERT_EQ(count, max_objs);
+
+  /* check that reading past seek also works */
+  cout << "seek_cursor=" << seek_cursor << std::endl;
+  it.seek(seek_cursor);
+  for (count = max_objs/2; count < max_objs; ++count, ++it) {
+    ASSERT_EQ(objs_order[count], it->get_oid());
+  }
+
+  /* seek to all cursors, check that we get expected obj */
+  for (auto& niter : ns_to_cursors) {
+    const string& ns = niter.first;
+    list<librados::ObjectCursor>& cursors = niter.second;
+
+    for (auto& cursor : cursors) {
+      cout << ": seek to " << cursor << std::endl;
+      it.seek(cursor);
+      ASSERT_EQ(cursor, it.get_cursor());
+      string& expected_oid = cursor_to_obj[cursor];
+      cout << ": it->get_cursor()=" << it.get_cursor() << " expected=" << cursor << std::endl;
+      cout << ": it->get_oid()=" << it->get_oid() << " expected=" << expected_oid << std::endl;
+      cout << ": it->get_nspace()=" << it->get_oid() << " expected=" << ns << std::endl;
+      ASSERT_EQ(expected_oid, it->get_oid());
+      ASSERT_EQ(it->get_nspace(), ns);
+    }
+  }
+}
+
+TEST_F(LibRadosListPP, ListObjectsCursorPP) {
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl;
+  bl.append(buf, sizeof(buf));
+
+  const int max_objs = 16;
+
+  for (int i=0; i<max_objs; ++i) {
+    stringstream ss;
+    ss << "ns" << i / 4;
+    ioctx.set_namespace(ss.str());
+    ASSERT_EQ(0, ioctx.write(stringify(i), bl, bl.length(), 0));
+  }
+
+  ioctx.set_namespace(all_nspaces);
+
+  librados::NObjectIterator it = ioctx.nobjects_begin();
+  std::map<librados::ObjectCursor, string> cursor_to_obj;
+
+  int count = 0;
+
+  for (; it != ioctx.nobjects_end(); ++it, ++count) {
+    librados::ObjectCursor cursor = it.get_cursor();
+    string oid = it->get_oid();
+    std::cout << oid << " " << it.get_pg_hash_position() << std::endl;
+    cout << ": oid=" << oid << " cursor=" << it.get_cursor() << std::endl;
+    cursor_to_obj[cursor] = oid;
+
+    it.seek(cursor);
+    cout << ": seek to " << cursor << std::endl;
+    ASSERT_EQ(oid, it->get_oid());
+    ASSERT_LT(count, max_objs); /* avoid infinite loops due to bad seek */
+  }
+
+  ASSERT_EQ(count, max_objs);
+
+  auto p = cursor_to_obj.rbegin();
+  it = ioctx.nobjects_begin();
+  while (p != cursor_to_obj.rend()) {
+    cout << ": seek to " << p->first << std::endl;
+    it.seek(p->first);
+    ASSERT_EQ(p->first, it.get_cursor());
+    cout << ": it->get_cursor()=" << it.get_cursor() << " expected=" << p->first << std::endl;
+    cout << ": it->get_oid()=" << it->get_oid() << " expected=" << p->second << std::endl;
+    ASSERT_EQ(p->second, it->get_oid());
+
+    librados::NObjectIterator it2 = ioctx.nobjects_begin(it.get_cursor());
+    ASSERT_EQ(it2->get_oid(), it->get_oid());
+
+    ++p;
+  }
+}
+
+TEST_F(LibRadosListECPP, ListObjectsPP) {
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+  NObjectIterator iter(ioctx.nobjects_begin());
+  bool foundit = false;
+  while (iter != ioctx.nobjects_end()) {
+    foundit = true;
+    ASSERT_EQ((*iter).get_oid(), "foo");
+    ++iter;
+  }
+  ASSERT_TRUE(foundit);
+}
+
+TEST_F(LibRadosListECPP, ListObjectsTwicePP) {
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+  NObjectIterator iter(ioctx.nobjects_begin());
+  bool foundit = false;
+  while (iter != ioctx.nobjects_end()) {
+    foundit = true;
+    ASSERT_EQ((*iter).get_oid(), "foo");
+    ++iter;
+  }
+  ASSERT_TRUE(foundit);
+  ++iter;
+  ASSERT_TRUE(iter == ioctx.nobjects_end());
+  foundit = false;
+  iter.seek(0);
+  while (iter != ioctx.nobjects_end()) {
+    foundit = true;
+    ASSERT_EQ((*iter).get_oid(), "foo");
+    ++iter;
+  }
+  ASSERT_TRUE(foundit);
+}
+
+TEST_F(LibRadosListECPP, ListObjectsCopyIterPP) {
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+
+  // make sure this is still valid after the original iterators are gone
+  NObjectIterator iter3;
+  {
+    NObjectIterator iter(ioctx.nobjects_begin());
+    NObjectIterator iter2(iter);
+    iter3 = iter2;
+    ASSERT_EQ((*iter).get_oid(), "foo");
+    ++iter;
+    ASSERT_TRUE(iter == ioctx.nobjects_end());
+    ++iter;
+    ASSERT_TRUE(iter == ioctx.nobjects_end());
+
+    ASSERT_EQ(iter2->get_oid(), "foo");
+    ASSERT_EQ(iter3->get_oid(), "foo");
+    ++iter2;
+    ASSERT_TRUE(iter2 == ioctx.nobjects_end());
+  }
+
+  ASSERT_EQ(iter3->get_oid(), "foo");
+  iter3 = iter3;
+  ASSERT_EQ(iter3->get_oid(), "foo");
+  ++iter3;
+  ASSERT_TRUE(iter3 == ioctx.nobjects_end());
+}
+
+TEST_F(LibRadosListECPP, ListObjectsEndIter) {
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+
+  NObjectIterator iter(ioctx.nobjects_begin());
+  NObjectIterator iter_end(ioctx.nobjects_end());
+  NObjectIterator iter_end2 = ioctx.nobjects_end();
+  ASSERT_TRUE(iter_end == iter_end2);
+  ASSERT_TRUE(iter_end == ioctx.nobjects_end());
+  ASSERT_TRUE(iter_end2 == ioctx.nobjects_end());
+
+  ASSERT_EQ(iter->get_oid(), "foo");
+  ++iter;
+  ASSERT_TRUE(iter == ioctx.nobjects_end());
+  ASSERT_TRUE(iter == iter_end);
+  ASSERT_TRUE(iter == iter_end2);
+  NObjectIterator iter2 = iter;
+  ASSERT_TRUE(iter2 == ioctx.nobjects_end());
+  ASSERT_TRUE(iter2 == iter_end);
+  ASSERT_TRUE(iter2 == iter_end2);
+}
+
+TEST_F(LibRadosListECPP, ListObjectsPPNS) {
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  // Create :foo1, :foo2, :foo3, n1:foo1, ns1:foo4, ns1:foo5, ns2:foo6, n2:foo7
+  ioctx.set_namespace("");
+  ASSERT_EQ(0, ioctx.write("foo1", bl1, sizeof(buf), 0));
+  ioctx.set_namespace("ns1");
+  ASSERT_EQ(0, ioctx.write("foo1", bl1, sizeof(buf), 0));
+  ioctx.set_namespace("");
+  ASSERT_EQ(0, ioctx.write("foo2", bl1, sizeof(buf), 0));
+  ASSERT_EQ(0, ioctx.write("foo3", bl1, sizeof(buf), 0));
+  ioctx.set_namespace("ns1");
+  ASSERT_EQ(0, ioctx.write("foo4", bl1, sizeof(buf), 0));
+  ASSERT_EQ(0, ioctx.write("foo5", bl1, sizeof(buf), 0));
+  ioctx.set_namespace("ns2");
+  ASSERT_EQ(0, ioctx.write("foo6", bl1, sizeof(buf), 0));
+  ASSERT_EQ(0, ioctx.write("foo7", bl1, sizeof(buf), 0));
+
+  std::set<std::string> def, ns1, ns2;
+  def.insert(std::string("foo1"));
+  def.insert(std::string("foo2"));
+  def.insert(std::string("foo3"));
+  ns1.insert(std::string("foo1"));
+  ns1.insert(std::string("foo4"));
+  ns1.insert(std::string("foo5"));
+  ns2.insert(std::string("foo6"));
+  ns2.insert(std::string("foo7"));
+
+  ioctx.set_namespace("");
+  check_listpp(def, ioctx, "");
+
+  ioctx.set_namespace("ns1");
+  check_listpp(ns1, ioctx, "ns1");
+
+  ioctx.set_namespace("ns2");
+  check_listpp(ns2, ioctx, "ns2");
+}
+
+TEST_F(LibRadosListECPP, ListObjectsManyPP) {
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl;
+  bl.append(buf, sizeof(buf));
+
+  for (int i=0; i<256; ++i) {
+    ASSERT_EQ(0, ioctx.write(stringify(i), bl, bl.length(), 0));
+  }
+
+  librados::NObjectIterator it = ioctx.nobjects_begin();
+  std::set<std::string> saw_obj;
+  std::set<int> saw_pg;
+  for (; it != ioctx.nobjects_end(); ++it) {
+    std::cout << it->get_oid()
+             << " " << it.get_pg_hash_position() << std::endl;
+    saw_obj.insert(it->get_oid());
+    saw_pg.insert(it.get_pg_hash_position());
+  }
+  std::cout << "saw " << saw_pg.size() << " pgs " << std::endl;
+
+  // make sure they are 0..n
+  for (unsigned i = 0; i < saw_pg.size(); ++i)
+    ASSERT_TRUE(saw_pg.count(i));
+}
+
+TEST_F(LibRadosListECPP, ListObjectsStartPP) {
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl;
+  bl.append(buf, sizeof(buf));
+
+  for (int i=0; i<16; ++i) {
+    ASSERT_EQ(0, ioctx.write(stringify(i), bl, bl.length(), 0));
+  }
+
+  librados::NObjectIterator it = ioctx.nobjects_begin();
+  std::map<int, std::set<std::string> > pg_to_obj;
+  for (; it != ioctx.nobjects_end(); ++it) {
+    std::cout << it->get_oid() << " " << it.get_pg_hash_position() << std::endl;
+    pg_to_obj[it.get_pg_hash_position()].insert(it->get_oid());
+  }
+
+  std::map<int, std::set<std::string> >::reverse_iterator p =
+    pg_to_obj.rbegin();
+  it = ioctx.nobjects_begin(p->first);
+  while (p != pg_to_obj.rend()) {
+    ASSERT_EQ((uint32_t)p->first, it.seek(p->first));
+    std::cout << "have " << it->get_oid() << " expect one of " << p->second << std::endl;
+    ASSERT_TRUE(p->second.count(it->get_oid()));
+    ++p;
+  }
+}
+
+TEST_F(LibRadosListPP, ListObjectsFilterPP) {
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist obj_content;
+  obj_content.append(buf, sizeof(buf));
+
+  std::string target_str = "content";
+
+  // Write xattr bare, no ::encod'ing
+  bufferlist target_val;
+  target_val.append(target_str);
+  bufferlist nontarget_val;
+  nontarget_val.append("rhubarb");
+
+  ASSERT_EQ(0, ioctx.write("has_xattr", obj_content, obj_content.length(), 0));
+  ASSERT_EQ(0, ioctx.write("has_wrong_xattr", obj_content, obj_content.length(), 0));
+  ASSERT_EQ(0, ioctx.write("no_xattr", obj_content, obj_content.length(), 0));
+
+  ASSERT_EQ(0, ioctx.setxattr("has_xattr", "theattr", target_val));
+  ASSERT_EQ(0, ioctx.setxattr("has_wrong_xattr", "theattr", nontarget_val));
+
+  bufferlist filter_bl;
+  std::string filter_name = "plain";
+  encode(filter_name, filter_bl);
+  encode("_theattr", filter_bl);
+  encode(target_str, filter_bl);
+
+  NObjectIterator iter(ioctx.nobjects_begin(filter_bl));
+  bool foundit = false;
+  int k = 0;
+  while (iter != ioctx.nobjects_end()) {
+    foundit = true;
+    // We should only see the object that matches the filter
+    ASSERT_EQ((*iter).get_oid(), "has_xattr");
+    // We should only see it once
+    ASSERT_EQ(k, 0);
+    ++iter;
+    ++k;
+  }
+  ASSERT_TRUE(foundit);
+}
+
+TEST_F(LibRadosListPP, EnumerateObjectsPP) {
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl;
+  bl.append(buf, sizeof(buf));
+
+  const uint32_t n_objects = 16;
+  for (unsigned i=0; i<n_objects; ++i) {
+    ASSERT_EQ(0, ioctx.write(stringify(i), bl, sizeof(buf), 0));
+  }
+
+  std::set<std::string> saw_obj;
+  ObjectCursor c = ioctx.object_list_begin();
+  ObjectCursor end = ioctx.object_list_end();
+  while(!ioctx.object_list_is_end(c))
+  {
+    std::vector<ObjectItem> result;
+    int r = ioctx.object_list(c, end, 12, {}, &result, &c);
+    ASSERT_GE(r, 0);
+    ASSERT_EQ(r, (int)result.size());
+    for (int i = 0; i < r; ++i) {
+      auto oid = result[i].oid;
+      if (saw_obj.count(oid)) {
+          std::cerr << "duplicate obj " << oid << std::endl;
+      }
+      ASSERT_FALSE(saw_obj.count(oid));
+      saw_obj.insert(oid);
+    }
+  }
+
+  for (unsigned i=0; i<n_objects; ++i) {
+    if (!saw_obj.count(stringify(i))) {
+        std::cerr << "missing object " << i << std::endl;
+    }
+    ASSERT_TRUE(saw_obj.count(stringify(i)));
+  }
+  ASSERT_EQ(n_objects, saw_obj.size());
+}
+
+TEST_F(LibRadosListPP, EnumerateObjectsSplitPP) {
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl;
+  bl.append(buf, sizeof(buf));
+
+  const uint32_t n_objects = 16;
+  for (unsigned i=0; i<n_objects; ++i) {
+    ASSERT_EQ(0, ioctx.write(stringify(i), bl, sizeof(buf), 0));
+  }
+
+  ObjectCursor begin = ioctx.object_list_begin();
+  ObjectCursor end = ioctx.object_list_end();
+
+  // Step through an odd number of shards
+  unsigned m = 5;
+  std::set<std::string> saw_obj;
+  for (unsigned n = 0; n < m; ++n) {
+      ObjectCursor shard_start;
+      ObjectCursor shard_end;
+
+      ioctx.object_list_slice(
+        begin,
+        end,
+        n,
+        m,
+        &shard_start,
+        &shard_end);
+
+      ObjectCursor c(shard_start);
+      while(c < shard_end)
+      {
+        std::vector<ObjectItem> result;
+        int r = ioctx.object_list(c, shard_end, 12, {}, &result, &c);
+        ASSERT_GE(r, 0);
+
+        for (const auto & i : result) {
+          const auto &oid = i.oid;
+          if (saw_obj.count(oid)) {
+              std::cerr << "duplicate obj " << oid << std::endl;
+          }
+          ASSERT_FALSE(saw_obj.count(oid));
+          saw_obj.insert(oid);
+        }
+      }
+  }
+
+  for (unsigned i=0; i<n_objects; ++i) {
+    if (!saw_obj.count(stringify(i))) {
+        std::cerr << "missing object " << i << std::endl;
+    }
+    ASSERT_TRUE(saw_obj.count(stringify(i)));
+  }
+  ASSERT_EQ(n_objects, saw_obj.size());
+}
+
+
+TEST_F(LibRadosListPP, EnumerateObjectsFilterPP) {
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist obj_content;
+  obj_content.append(buf, sizeof(buf));
+
+  std::string target_str = "content";
+
+  // Write xattr bare, no ::encod'ing
+  bufferlist target_val;
+  target_val.append(target_str);
+  bufferlist nontarget_val;
+  nontarget_val.append("rhubarb");
+
+  ASSERT_EQ(0, ioctx.write("has_xattr", obj_content, obj_content.length(), 0));
+  ASSERT_EQ(0, ioctx.write("has_wrong_xattr", obj_content, obj_content.length(), 0));
+  ASSERT_EQ(0, ioctx.write("no_xattr", obj_content, obj_content.length(), 0));
+
+  ASSERT_EQ(0, ioctx.setxattr("has_xattr", "theattr", target_val));
+  ASSERT_EQ(0, ioctx.setxattr("has_wrong_xattr", "theattr", nontarget_val));
+
+  bufferlist filter_bl;
+  std::string filter_name = "plain";
+  encode(filter_name, filter_bl);
+  encode("_theattr", filter_bl);
+  encode(target_str, filter_bl);
+
+  ObjectCursor c = ioctx.object_list_begin();
+  ObjectCursor end = ioctx.object_list_end();
+  bool foundit = false;
+  while(!ioctx.object_list_is_end(c))
+  {
+    std::vector<ObjectItem> result;
+    int r = ioctx.object_list(c, end, 12, filter_bl, &result, &c);
+    ASSERT_GE(r, 0);
+    ASSERT_EQ(r, (int)result.size());
+    for (int i = 0; i < r; ++i) {
+      auto oid = result[i].oid;
+      // We should only see the object that matches the filter
+      ASSERT_EQ(oid, "has_xattr");
+      // We should only see it once
+      ASSERT_FALSE(foundit);
+      foundit = true;
+    }
+  }
+  ASSERT_TRUE(foundit);
+}
index ef1033f5e66014ac70eb8bfde8d34b45df77cb55..53ed300e7baf4f57e3874cdb054e1815745ce2ba 100644 (file)
@@ -1,5 +1,4 @@
 #include "include/rados/librados.h"
-#include "include/rados/librados.hpp"
 #include "test/librados/test.h"
 #include "test/librados/TestCase.h"
 #include "cls/lock/cls_lock_client.h"
 #include <sys/time.h>
 
 using namespace std::chrono_literals;
-using namespace librados;
 
 typedef RadosTest LibRadosLock;
-typedef RadosTestPP LibRadosLockPP;
 typedef RadosTestEC LibRadosLockEC;
-typedef RadosTestECPP LibRadosLockECPP;
 
 
-template<class Rep, class Period, typename Func, typename... Args,
-         typename Return = std::result_of_t<Func&&(Args&&...)>>
-Return wait_until(const std::chrono::duration<Rep, Period>& rel_time,
-                const std::chrono::duration<Rep, Period>& step,
-                const Return& expected,
-                Func&& func, Args&&... args)
-{
-  std::this_thread::sleep_for(rel_time - step);
-  for (auto& s : {step, step}) {
-    if (!s.count()) {
-      break;
-    }
-    auto ret = func(std::forward<Args>(args)...);
-    if (ret == expected) {
-      return ret;
-    }
-    std::this_thread::sleep_for(s);
-  }
-  return func(std::forward<Args>(args)...);
-}
-
 TEST_F(LibRadosLock, LockExclusive) {
   ASSERT_EQ(0, rados_lock_exclusive(ioctx, "foo", "TestLock1", "Cookie", "", NULL,  0));
   ASSERT_EQ(-EEXIST, rados_lock_exclusive(ioctx, "foo", "TestLock1", "Cookie", "", NULL, 0));
 }
 
-TEST_F(LibRadosLockPP, LockExclusivePP) {
-  ASSERT_EQ(0, ioctx.lock_exclusive("foo", "TestLockPP1", "Cookie", "", NULL,  0));
-  ASSERT_EQ(-EEXIST, ioctx.lock_exclusive("foo", "TestLockPP1", "Cookie", "", NULL, 0));
-}
-
 TEST_F(LibRadosLock, LockShared) {
   ASSERT_EQ(0, rados_lock_shared(ioctx, "foo", "TestLock2", "Cookie", "Tag", "", NULL, 0));
   ASSERT_EQ(-EEXIST, rados_lock_shared(ioctx, "foo", "TestLock2", "Cookie", "Tag", "", NULL, 0));
 }
 
-TEST_F(LibRadosLockPP, LockSharedPP) {
-  ASSERT_EQ(0, ioctx.lock_shared("foo", "TestLockPP2", "Cookie", "Tag", "", NULL, 0));
-  ASSERT_EQ(-EEXIST, ioctx.lock_shared("foo", "TestLockPP2", "Cookie", "Tag", "", NULL, 0));
-}
-
 TEST_F(LibRadosLock, LockExclusiveDur) {
   struct timeval tv;
   tv.tv_sec = 1;
@@ -73,18 +38,6 @@ TEST_F(LibRadosLock, LockExclusiveDur) {
   ASSERT_EQ(expected, wait_until(1.0s, 0.1s, expected, lock_exclusive, nullptr));
 }
 
-TEST_F(LibRadosLockPP, LockExclusiveDurPP) {
-  struct timeval tv;
-  tv.tv_sec = 1;
-  tv.tv_usec = 0;
-  auto lock_exclusive = [this](timeval* tv) {
-    return ioctx.lock_exclusive("foo", "TestLockPP3", "Cookie", "", tv, 0);
-  };
-  constexpr int expected = 0;
-  ASSERT_EQ(expected, lock_exclusive(&tv));
-  ASSERT_EQ(expected, wait_until(1.0s, 0.1s, expected, lock_exclusive, nullptr));
-}
-
 TEST_F(LibRadosLock, LockSharedDur) {
   struct timeval tv;
   tv.tv_sec = 1;
@@ -97,17 +50,6 @@ TEST_F(LibRadosLock, LockSharedDur) {
   ASSERT_EQ(expected, wait_until(1.0s, 0.1s, expected, lock_shared, nullptr));
 }
 
-TEST_F(LibRadosLockPP, LockSharedDurPP) {
-  struct timeval tv;
-  tv.tv_sec = 1;
-  tv.tv_usec = 0;
-  auto lock_shared = [this](timeval* tv) {
-    return ioctx.lock_shared("foo", "TestLockPP4", "Cookie", "Tag", "", tv, 0);
-  };
-  constexpr int expected = 0;
-  ASSERT_EQ(expected, lock_shared(&tv));
-  ASSERT_EQ(expected, wait_until(1.0s, 0.1s, expected, lock_shared, nullptr));
-}
 
 TEST_F(LibRadosLock, LockMayRenew) {
   ASSERT_EQ(0, rados_lock_exclusive(ioctx, "foo", "TestLock5", "Cookie", "", NULL, 0));
@@ -115,24 +57,12 @@ TEST_F(LibRadosLock, LockMayRenew) {
   ASSERT_EQ(0, rados_lock_exclusive(ioctx, "foo", "TestLock5", "Cookie", "", NULL, LOCK_FLAG_MAY_RENEW));
 }
 
-TEST_F(LibRadosLockPP, LockMayRenewPP) {
-  ASSERT_EQ(0, ioctx.lock_exclusive("foo", "TestLockPP5", "Cookie", "", NULL, 0));
-  ASSERT_EQ(-EEXIST, ioctx.lock_exclusive("foo", "TestLockPP5", "Cookie", "", NULL, 0));
-  ASSERT_EQ(0, ioctx.lock_exclusive("foo", "TestLockPP5", "Cookie", "", NULL, LOCK_FLAG_MAY_RENEW));
-}
-
 TEST_F(LibRadosLock, Unlock) {
   ASSERT_EQ(0, rados_lock_exclusive(ioctx, "foo", "TestLock6", "Cookie", "", NULL, 0));
   ASSERT_EQ(0, rados_unlock(ioctx, "foo", "TestLock6", "Cookie"));
   ASSERT_EQ(0, rados_lock_exclusive(ioctx, "foo", "TestLock6", "Cookie", "", NULL,  0));
 }
 
-TEST_F(LibRadosLockPP, UnlockPP) {
-  ASSERT_EQ(0, ioctx.lock_exclusive("foo", "TestLockPP6", "Cookie", "", NULL, 0));
-  ASSERT_EQ(0, ioctx.unlock("foo", "TestLockPP6", "Cookie"));
-  ASSERT_EQ(0, ioctx.lock_exclusive("foo", "TestLockPP6", "Cookie", "", NULL, 0));
-}
-
 TEST_F(LibRadosLock, ListLockers) {
   int exclusive;
   char tag[1024];
@@ -165,31 +95,6 @@ TEST_F(LibRadosLock, ListLockers) {
   ASSERT_EQ(strlen("Cookie") + 1, cookies_len);
 }
 
-TEST_F(LibRadosLockPP, ListLockersPP) {
-  std::stringstream sstm;
-  sstm << "client." << cluster.get_instance_id();
-  std::string me = sstm.str();
-  ASSERT_EQ(0, ioctx.lock_shared("foo", "TestLockPP7", "Cookie", "Tag", "", NULL, 0));
-  ASSERT_EQ(0, ioctx.unlock("foo", "TestLockPP7", "Cookie"));
-  {
-    int exclusive;
-    std::string tag;
-    std::list<librados::locker_t> lockers;
-    ASSERT_EQ(0, ioctx.list_lockers("foo", "TestLockPP7", &exclusive, &tag, &lockers));
-  }
-  ASSERT_EQ(0, ioctx.lock_shared("foo", "TestLockPP7", "Cookie", "Tag", "", NULL, 0));
-  {
-    int exclusive;
-    std::string tag;
-    std::list<librados::locker_t> lockers;
-    ASSERT_EQ(1, ioctx.list_lockers("foo", "TestLockPP7", &exclusive, &tag, &lockers));
-    std::list<librados::locker_t>::iterator it = lockers.begin();
-    ASSERT_FALSE(lockers.end() == it);
-    ASSERT_EQ(me, it->client);
-    ASSERT_EQ("Cookie", it->cookie);
-  }
-}
-
 TEST_F(LibRadosLock, BreakLock) {
   int exclusive;
   char tag[1024];
@@ -215,43 +120,17 @@ TEST_F(LibRadosLock, BreakLock) {
   ASSERT_EQ(0, rados_break_lock(ioctx, "foo", "TestLock8", clients, "Cookie"));
 }
 
-TEST_F(LibRadosLockPP, BreakLockPP) {
-  int exclusive;
-  std::string tag;
-  std::list<librados::locker_t> lockers;
-  std::stringstream sstm;
-  sstm << "client." << cluster.get_instance_id();
-  std::string me = sstm.str();
-  ASSERT_EQ(0, ioctx.lock_exclusive("foo", "TestLockPP8", "Cookie",  "", NULL, 0));
-  ASSERT_EQ(1, ioctx.list_lockers("foo", "TestLockPP8", &exclusive, &tag, &lockers));
-  std::list<librados::locker_t>::iterator it = lockers.begin();
-  ASSERT_FALSE(lockers.end() == it);
-  ASSERT_EQ(me, it->client);
-  ASSERT_EQ("Cookie", it->cookie);
-  ASSERT_EQ(0, ioctx.break_lock("foo", "TestLockPP8", it->client, "Cookie"));
-}
-
 // EC testing
 TEST_F(LibRadosLockEC, LockExclusive) {
   ASSERT_EQ(0, rados_lock_exclusive(ioctx, "foo", "TestLockEC1", "Cookie", "", NULL,  0));
   ASSERT_EQ(-EEXIST, rados_lock_exclusive(ioctx, "foo", "TestLockEC1", "Cookie", "", NULL, 0));
 }
 
-TEST_F(LibRadosLockECPP, LockExclusivePP) {
-  ASSERT_EQ(0, ioctx.lock_exclusive("foo", "TestLockECPP1", "Cookie", "", NULL,  0));
-  ASSERT_EQ(-EEXIST, ioctx.lock_exclusive("foo", "TestLockECPP1", "Cookie", "", NULL, 0));
-}
-
 TEST_F(LibRadosLockEC, LockShared) {
   ASSERT_EQ(0, rados_lock_shared(ioctx, "foo", "TestLockEC2", "Cookie", "Tag", "", NULL, 0));
   ASSERT_EQ(-EEXIST, rados_lock_shared(ioctx, "foo", "TestLockEC2", "Cookie", "Tag", "", NULL, 0));
 }
 
-TEST_F(LibRadosLockECPP, LockSharedPP) {
-  ASSERT_EQ(0, ioctx.lock_shared("foo", "TestLockECPP2", "Cookie", "Tag", "", NULL, 0));
-  ASSERT_EQ(-EEXIST, ioctx.lock_shared("foo", "TestLockECPP2", "Cookie", "Tag", "", NULL, 0));
-}
-
 TEST_F(LibRadosLockEC, LockExclusiveDur) {
   struct timeval tv;
   tv.tv_sec = 1;
@@ -264,18 +143,6 @@ TEST_F(LibRadosLockEC, LockExclusiveDur) {
   ASSERT_EQ(expected, wait_until(1.0s, 0.1s, expected, lock_exclusive, nullptr));
 }
 
-TEST_F(LibRadosLockECPP, LockExclusiveDurPP) {
-  struct timeval tv;
-  tv.tv_sec = 1;
-  tv.tv_usec = 0;
-  auto lock_exclusive = [this](timeval* tv) {
-    return ioctx.lock_exclusive("foo", "TestLockECPP3", "Cookie", "", tv, 0);
-  };
-  constexpr int expected = 0;
-  ASSERT_EQ(expected, lock_exclusive(&tv));
-  ASSERT_EQ(expected, wait_until(1.0s, 0.1s, expected, lock_exclusive, nullptr));
-}
-
 TEST_F(LibRadosLockEC, LockSharedDur) {
   struct timeval tv;
   tv.tv_sec = 1;
@@ -288,17 +155,6 @@ TEST_F(LibRadosLockEC, LockSharedDur) {
   ASSERT_EQ(expected, wait_until(1.0s, 0.1s, expected, lock_shared, nullptr));
 }
 
-TEST_F(LibRadosLockECPP, LockSharedDurPP) {
-  struct timeval tv;
-  tv.tv_sec = 1;
-  tv.tv_usec = 0;
-  auto lock_shared = [this](timeval* tv) {
-    return ioctx.lock_shared("foo", "TestLockECPP4", "Cookie", "Tag", "", tv, 0);
-  };
-  const int expected = 0;
-  ASSERT_EQ(expected, lock_shared(&tv));
-  ASSERT_EQ(expected, wait_until(1.0s, 0.1s, expected, lock_shared, nullptr));
-}
 
 TEST_F(LibRadosLockEC, LockMayRenew) {
   ASSERT_EQ(0, rados_lock_exclusive(ioctx, "foo", "TestLockEC5", "Cookie", "", NULL, 0));
@@ -306,24 +162,12 @@ TEST_F(LibRadosLockEC, LockMayRenew) {
   ASSERT_EQ(0, rados_lock_exclusive(ioctx, "foo", "TestLockEC5", "Cookie", "", NULL, LOCK_FLAG_MAY_RENEW));
 }
 
-TEST_F(LibRadosLockECPP, LockMayRenewPP) {
-  ASSERT_EQ(0, ioctx.lock_exclusive("foo", "TestLockECPP5", "Cookie", "", NULL, 0));
-  ASSERT_EQ(-EEXIST, ioctx.lock_exclusive("foo", "TestLockECPP5", "Cookie", "", NULL, 0));
-  ASSERT_EQ(0, ioctx.lock_exclusive("foo", "TestLockECPP5", "Cookie", "", NULL, LOCK_FLAG_MAY_RENEW));
-}
-
 TEST_F(LibRadosLockEC, Unlock) {
   ASSERT_EQ(0, rados_lock_exclusive(ioctx, "foo", "TestLockEC6", "Cookie", "", NULL, 0));
   ASSERT_EQ(0, rados_unlock(ioctx, "foo", "TestLockEC6", "Cookie"));
   ASSERT_EQ(0, rados_lock_exclusive(ioctx, "foo", "TestLockEC6", "Cookie", "", NULL,  0));
 }
 
-TEST_F(LibRadosLockECPP, UnlockPP) {
-  ASSERT_EQ(0, ioctx.lock_exclusive("foo", "TestLockECPP6", "Cookie", "", NULL, 0));
-  ASSERT_EQ(0, ioctx.unlock("foo", "TestLockECPP6", "Cookie"));
-  ASSERT_EQ(0, ioctx.lock_exclusive("foo", "TestLockECPP6", "Cookie", "", NULL, 0));
-}
-
 TEST_F(LibRadosLockEC, ListLockers) {
   int exclusive;
   char tag[1024];
@@ -356,31 +200,6 @@ TEST_F(LibRadosLockEC, ListLockers) {
   ASSERT_EQ(strlen("Cookie") + 1, cookies_len);
 }
 
-TEST_F(LibRadosLockECPP, ListLockersPP) {
-  std::stringstream sstm;
-  sstm << "client." << cluster.get_instance_id();
-  std::string me = sstm.str();
-  ASSERT_EQ(0, ioctx.lock_shared("foo", "TestLockECPP7", "Cookie", "Tag", "", NULL, 0));
-  ASSERT_EQ(0, ioctx.unlock("foo", "TestLockECPP7", "Cookie"));
-  {
-    int exclusive;
-    std::string tag;
-    std::list<librados::locker_t> lockers;
-    ASSERT_EQ(0, ioctx.list_lockers("foo", "TestLockECPP7", &exclusive, &tag, &lockers));
-  }
-  ASSERT_EQ(0, ioctx.lock_shared("foo", "TestLockECPP7", "Cookie", "Tag", "", NULL, 0));
-  {
-    int exclusive;
-    std::string tag;
-    std::list<librados::locker_t> lockers;
-    ASSERT_EQ(1, ioctx.list_lockers("foo", "TestLockECPP7", &exclusive, &tag, &lockers));
-    std::list<librados::locker_t>::iterator it = lockers.begin();
-    ASSERT_FALSE(lockers.end() == it);
-    ASSERT_EQ(me, it->client);
-    ASSERT_EQ("Cookie", it->cookie);
-  }
-}
-
 TEST_F(LibRadosLockEC, BreakLock) {
   int exclusive;
   char tag[1024];
@@ -406,18 +225,3 @@ TEST_F(LibRadosLockEC, BreakLock) {
   ASSERT_EQ(0, rados_break_lock(ioctx, "foo", "TestLockEC8", clients, "Cookie"));
 }
 
-TEST_F(LibRadosLockECPP, BreakLockPP) {
-  int exclusive;
-  std::string tag;
-  std::list<librados::locker_t> lockers;
-  std::stringstream sstm;
-  sstm << "client." << cluster.get_instance_id();
-  std::string me = sstm.str();
-  ASSERT_EQ(0, ioctx.lock_exclusive("foo", "TestLockECPP8", "Cookie",  "", NULL, 0));
-  ASSERT_EQ(1, ioctx.list_lockers("foo", "TestLockECPP8", &exclusive, &tag, &lockers));
-  std::list<librados::locker_t>::iterator it = lockers.begin();
-  ASSERT_FALSE(lockers.end() == it);
-  ASSERT_EQ(me, it->client);
-  ASSERT_EQ("Cookie", it->cookie);
-  ASSERT_EQ(0, ioctx.break_lock("foo", "TestLockECPP8", it->client, "Cookie"));
-}
diff --git a/src/test/librados/lock_cxx.cc b/src/test/librados/lock_cxx.cc
new file mode 100644 (file)
index 0000000..3007b22
--- /dev/null
@@ -0,0 +1,193 @@
+#include <algorithm>
+#include <chrono>
+#include <thread>
+#include <errno.h>
+#include <sys/time.h>
+#include "gtest/gtest.h"
+
+#include "include/rados/librados.hpp"
+#include "cls/lock/cls_lock_client.h"
+
+#include "test/librados/test_cxx.h"
+#include "test/librados/testcase_cxx.h"
+
+using namespace std::chrono_literals;
+using namespace librados;
+
+typedef RadosTestPP LibRadosLockPP;
+typedef RadosTestECPP LibRadosLockECPP;
+
+TEST_F(LibRadosLockPP, LockExclusivePP) {
+  ASSERT_EQ(0, ioctx.lock_exclusive("foo", "TestLockPP1", "Cookie", "", NULL,  0));
+  ASSERT_EQ(-EEXIST, ioctx.lock_exclusive("foo", "TestLockPP1", "Cookie", "", NULL, 0));
+}
+
+TEST_F(LibRadosLockPP, LockSharedPP) {
+  ASSERT_EQ(0, ioctx.lock_shared("foo", "TestLockPP2", "Cookie", "Tag", "", NULL, 0));
+  ASSERT_EQ(-EEXIST, ioctx.lock_shared("foo", "TestLockPP2", "Cookie", "Tag", "", NULL, 0));
+}
+
+TEST_F(LibRadosLockPP, LockExclusiveDurPP) {
+  struct timeval tv;
+  tv.tv_sec = 1;
+  tv.tv_usec = 0;
+  auto lock_exclusive = [this](timeval* tv) {
+    return ioctx.lock_exclusive("foo", "TestLockPP3", "Cookie", "", tv, 0);
+  };
+  constexpr int expected = 0;
+  ASSERT_EQ(expected, lock_exclusive(&tv));
+  ASSERT_EQ(expected, wait_until(1.0s, 0.1s, expected, lock_exclusive, nullptr));
+}
+
+TEST_F(LibRadosLockPP, LockSharedDurPP) {
+  struct timeval tv;
+  tv.tv_sec = 1;
+  tv.tv_usec = 0;
+  auto lock_shared = [this](timeval* tv) {
+    return ioctx.lock_shared("foo", "TestLockPP4", "Cookie", "Tag", "", tv, 0);
+  };
+  constexpr int expected = 0;
+  ASSERT_EQ(expected, lock_shared(&tv));
+  ASSERT_EQ(expected, wait_until(1.0s, 0.1s, expected, lock_shared, nullptr));
+}
+
+TEST_F(LibRadosLockPP, LockMayRenewPP) {
+  ASSERT_EQ(0, ioctx.lock_exclusive("foo", "TestLockPP5", "Cookie", "", NULL, 0));
+  ASSERT_EQ(-EEXIST, ioctx.lock_exclusive("foo", "TestLockPP5", "Cookie", "", NULL, 0));
+  ASSERT_EQ(0, ioctx.lock_exclusive("foo", "TestLockPP5", "Cookie", "", NULL, LOCK_FLAG_MAY_RENEW));
+}
+
+TEST_F(LibRadosLockPP, UnlockPP) {
+  ASSERT_EQ(0, ioctx.lock_exclusive("foo", "TestLockPP6", "Cookie", "", NULL, 0));
+  ASSERT_EQ(0, ioctx.unlock("foo", "TestLockPP6", "Cookie"));
+  ASSERT_EQ(0, ioctx.lock_exclusive("foo", "TestLockPP6", "Cookie", "", NULL, 0));
+}
+
+TEST_F(LibRadosLockPP, ListLockersPP) {
+  std::stringstream sstm;
+  sstm << "client." << cluster.get_instance_id();
+  std::string me = sstm.str();
+  ASSERT_EQ(0, ioctx.lock_shared("foo", "TestLockPP7", "Cookie", "Tag", "", NULL, 0));
+  ASSERT_EQ(0, ioctx.unlock("foo", "TestLockPP7", "Cookie"));
+  {
+    int exclusive;
+    std::string tag;
+    std::list<librados::locker_t> lockers;
+    ASSERT_EQ(0, ioctx.list_lockers("foo", "TestLockPP7", &exclusive, &tag, &lockers));
+  }
+  ASSERT_EQ(0, ioctx.lock_shared("foo", "TestLockPP7", "Cookie", "Tag", "", NULL, 0));
+  {
+    int exclusive;
+    std::string tag;
+    std::list<librados::locker_t> lockers;
+    ASSERT_EQ(1, ioctx.list_lockers("foo", "TestLockPP7", &exclusive, &tag, &lockers));
+    std::list<librados::locker_t>::iterator it = lockers.begin();
+    ASSERT_FALSE(lockers.end() == it);
+    ASSERT_EQ(me, it->client);
+    ASSERT_EQ("Cookie", it->cookie);
+  }
+}
+
+TEST_F(LibRadosLockPP, BreakLockPP) {
+  int exclusive;
+  std::string tag;
+  std::list<librados::locker_t> lockers;
+  std::stringstream sstm;
+  sstm << "client." << cluster.get_instance_id();
+  std::string me = sstm.str();
+  ASSERT_EQ(0, ioctx.lock_exclusive("foo", "TestLockPP8", "Cookie",  "", NULL, 0));
+  ASSERT_EQ(1, ioctx.list_lockers("foo", "TestLockPP8", &exclusive, &tag, &lockers));
+  std::list<librados::locker_t>::iterator it = lockers.begin();
+  ASSERT_FALSE(lockers.end() == it);
+  ASSERT_EQ(me, it->client);
+  ASSERT_EQ("Cookie", it->cookie);
+  ASSERT_EQ(0, ioctx.break_lock("foo", "TestLockPP8", it->client, "Cookie"));
+}
+
+// EC testing
+TEST_F(LibRadosLockECPP, LockExclusivePP) {
+  ASSERT_EQ(0, ioctx.lock_exclusive("foo", "TestLockECPP1", "Cookie", "", NULL,  0));
+  ASSERT_EQ(-EEXIST, ioctx.lock_exclusive("foo", "TestLockECPP1", "Cookie", "", NULL, 0));
+}
+
+TEST_F(LibRadosLockECPP, LockSharedPP) {
+  ASSERT_EQ(0, ioctx.lock_shared("foo", "TestLockECPP2", "Cookie", "Tag", "", NULL, 0));
+  ASSERT_EQ(-EEXIST, ioctx.lock_shared("foo", "TestLockECPP2", "Cookie", "Tag", "", NULL, 0));
+}
+
+TEST_F(LibRadosLockECPP, LockExclusiveDurPP) {
+  struct timeval tv;
+  tv.tv_sec = 1;
+  tv.tv_usec = 0;
+  auto lock_exclusive = [this](timeval* tv) {
+    return ioctx.lock_exclusive("foo", "TestLockECPP3", "Cookie", "", tv, 0);
+  };
+  constexpr int expected = 0;
+  ASSERT_EQ(expected, lock_exclusive(&tv));
+  ASSERT_EQ(expected, wait_until(1.0s, 0.1s, expected, lock_exclusive, nullptr));
+}
+
+TEST_F(LibRadosLockECPP, LockSharedDurPP) {
+  struct timeval tv;
+  tv.tv_sec = 1;
+  tv.tv_usec = 0;
+  auto lock_shared = [this](timeval* tv) {
+    return ioctx.lock_shared("foo", "TestLockECPP4", "Cookie", "Tag", "", tv, 0);
+  };
+  const int expected = 0;
+  ASSERT_EQ(expected, lock_shared(&tv));
+  ASSERT_EQ(expected, wait_until(1.0s, 0.1s, expected, lock_shared, nullptr));
+}
+
+TEST_F(LibRadosLockECPP, LockMayRenewPP) {
+  ASSERT_EQ(0, ioctx.lock_exclusive("foo", "TestLockECPP5", "Cookie", "", NULL, 0));
+  ASSERT_EQ(-EEXIST, ioctx.lock_exclusive("foo", "TestLockECPP5", "Cookie", "", NULL, 0));
+  ASSERT_EQ(0, ioctx.lock_exclusive("foo", "TestLockECPP5", "Cookie", "", NULL, LOCK_FLAG_MAY_RENEW));
+}
+
+TEST_F(LibRadosLockECPP, UnlockPP) {
+  ASSERT_EQ(0, ioctx.lock_exclusive("foo", "TestLockECPP6", "Cookie", "", NULL, 0));
+  ASSERT_EQ(0, ioctx.unlock("foo", "TestLockECPP6", "Cookie"));
+  ASSERT_EQ(0, ioctx.lock_exclusive("foo", "TestLockECPP6", "Cookie", "", NULL, 0));
+}
+
+TEST_F(LibRadosLockECPP, ListLockersPP) {
+  std::stringstream sstm;
+  sstm << "client." << cluster.get_instance_id();
+  std::string me = sstm.str();
+  ASSERT_EQ(0, ioctx.lock_shared("foo", "TestLockECPP7", "Cookie", "Tag", "", NULL, 0));
+  ASSERT_EQ(0, ioctx.unlock("foo", "TestLockECPP7", "Cookie"));
+  {
+    int exclusive;
+    std::string tag;
+    std::list<librados::locker_t> lockers;
+    ASSERT_EQ(0, ioctx.list_lockers("foo", "TestLockECPP7", &exclusive, &tag, &lockers));
+  }
+  ASSERT_EQ(0, ioctx.lock_shared("foo", "TestLockECPP7", "Cookie", "Tag", "", NULL, 0));
+  {
+    int exclusive;
+    std::string tag;
+    std::list<librados::locker_t> lockers;
+    ASSERT_EQ(1, ioctx.list_lockers("foo", "TestLockECPP7", &exclusive, &tag, &lockers));
+    std::list<librados::locker_t>::iterator it = lockers.begin();
+    ASSERT_FALSE(lockers.end() == it);
+    ASSERT_EQ(me, it->client);
+    ASSERT_EQ("Cookie", it->cookie);
+  }
+}
+
+TEST_F(LibRadosLockECPP, BreakLockPP) {
+  int exclusive;
+  std::string tag;
+  std::list<librados::locker_t> lockers;
+  std::stringstream sstm;
+  sstm << "client." << cluster.get_instance_id();
+  std::string me = sstm.str();
+  ASSERT_EQ(0, ioctx.lock_exclusive("foo", "TestLockECPP8", "Cookie",  "", NULL, 0));
+  ASSERT_EQ(1, ioctx.list_lockers("foo", "TestLockECPP8", &exclusive, &tag, &lockers));
+  std::list<librados::locker_t>::iterator it = lockers.begin();
+  ASSERT_FALSE(lockers.end() == it);
+  ASSERT_EQ(me, it->client);
+  ASSERT_EQ("Cookie", it->cookie);
+  ASSERT_EQ(0, ioctx.break_lock("foo", "TestLockECPP8", it->client, "Cookie"));
+}
index 50f87186566560fe8cc599e5cbbe9653c649ce96..5f7fdd889f5a5accc05c1663129ea83225384ce2 100644 (file)
@@ -29,19 +29,12 @@ using std::ostringstream;
 using std::string;
 
 typedef RadosTest LibRadosMisc;
-typedef RadosTestPP LibRadosMiscPP;
-typedef RadosTestECPP LibRadosMiscECPP;
 
 TEST(LibRadosMiscVersion, Version) {
   int major, minor, extra;
   rados_version(&major, &minor, &extra);
 }
 
-TEST(LibRadosMiscVersion, VersionPP) {
-  int major, minor, extra;
-  Rados::version(&major, &minor, &extra);
-}
-
 static void test_rados_log_cb(void *arg,
                               const char *line,
                               const char *who,
@@ -161,350 +154,6 @@ TEST_F(LibRadosMisc, ClusterFSID) {
             (size_t)rados_cluster_fsid(cluster, fsid, sizeof(fsid)));
 }
 
-TEST_F(LibRadosMiscPP, WaitOSDMapPP) {
-  ASSERT_EQ(0, cluster.wait_for_latest_osdmap());
-}
-
-TEST_F(LibRadosMiscPP, LongNamePP) {
-  bufferlist bl;
-  bl.append("content");
-  int maxlen = g_conf()->osd_max_object_name_len;
-  ASSERT_EQ(0, ioctx.write(string(maxlen/2, 'a').c_str(), bl, bl.length(), 0));
-  ASSERT_EQ(0, ioctx.write(string(maxlen-1, 'a').c_str(), bl, bl.length(), 0));
-  ASSERT_EQ(0, ioctx.write(string(maxlen, 'a').c_str(), bl, bl.length(), 0));
-  ASSERT_EQ(-ENAMETOOLONG, ioctx.write(string(maxlen+1, 'a').c_str(), bl, bl.length(), 0));
-  ASSERT_EQ(-ENAMETOOLONG, ioctx.write(string(maxlen*2, 'a').c_str(), bl, bl.length(), 0));
-}
-
-TEST_F(LibRadosMiscPP, LongLocatorPP) {
-  bufferlist bl;
-  bl.append("content");
-  int maxlen = g_conf()->osd_max_object_name_len;
-  ioctx.locator_set_key(
-    string((maxlen/2), 'a'));
-  ASSERT_EQ(
-    0,
-    ioctx.write(
-      string("a").c_str(),
-      bl, bl.length(), 0));
-  ioctx.locator_set_key(
-    string(maxlen - 1, 'a'));
-  ASSERT_EQ(
-    0,
-    ioctx.write(
-      string("a").c_str(),
-      bl, bl.length(), 0));
-  ioctx.locator_set_key(
-    string(maxlen, 'a'));
-  ASSERT_EQ(
-    0,
-    ioctx.write(
-      string("a").c_str(),
-      bl, bl.length(), 0));
-  ioctx.locator_set_key(
-    string(maxlen+1, 'a'));
-  ASSERT_EQ(
-    -ENAMETOOLONG,
-    ioctx.write(
-      string("a").c_str(),
-      bl, bl.length(), 0));
-  ioctx.locator_set_key(
-    string((maxlen*2), 'a'));
-  ASSERT_EQ(
-    -ENAMETOOLONG,
-    ioctx.write(
-      string("a").c_str(),
-      bl, bl.length(), 0));
-}
-
-TEST_F(LibRadosMiscPP, LongNSpacePP) {
-  bufferlist bl;
-  bl.append("content");
-  int maxlen = g_conf()->osd_max_object_namespace_len;
-  ioctx.set_namespace(
-    string((maxlen/2), 'a'));
-  ASSERT_EQ(
-    0,
-    ioctx.write(
-      string("a").c_str(),
-      bl, bl.length(), 0));
-  ioctx.set_namespace(
-    string(maxlen - 1, 'a'));
-  ASSERT_EQ(
-    0,
-    ioctx.write(
-      string("a").c_str(),
-      bl, bl.length(), 0));
-  ioctx.set_namespace(
-    string(maxlen, 'a'));
-  ASSERT_EQ(
-    0,
-    ioctx.write(
-      string("a").c_str(),
-      bl, bl.length(), 0));
-  ioctx.set_namespace(
-    string(maxlen+1, 'a'));
-  ASSERT_EQ(
-    -ENAMETOOLONG,
-    ioctx.write(
-      string("a").c_str(),
-      bl, bl.length(), 0));
-  ioctx.set_namespace(
-    string((maxlen*2), 'a'));
-  ASSERT_EQ(
-    -ENAMETOOLONG,
-    ioctx.write(
-      string("a").c_str(),
-      bl, bl.length(), 0));
-}
-
-TEST_F(LibRadosMiscPP, LongAttrNamePP) {
-  bufferlist bl;
-  bl.append("content");
-  int maxlen = g_conf()->osd_max_attr_name_len;
-  ASSERT_EQ(0, ioctx.setxattr("bigattrobj", string(maxlen/2, 'a').c_str(), bl));
-  ASSERT_EQ(0, ioctx.setxattr("bigattrobj", string(maxlen-1, 'a').c_str(), bl));
-  ASSERT_EQ(0, ioctx.setxattr("bigattrobj", string(maxlen, 'a').c_str(), bl));
-  ASSERT_EQ(-ENAMETOOLONG, ioctx.setxattr("bigattrobj", string(maxlen+1, 'a').c_str(), bl));
-  ASSERT_EQ(-ENAMETOOLONG, ioctx.setxattr("bigattrobj", string(maxlen*2, 'a').c_str(), bl));
-}
-
-static std::string read_key_from_tmap(IoCtx& ioctx, const std::string &obj,
-                                     const std::string &key)
-{
-  bufferlist bl;
-  int r = ioctx.read(obj, bl, 0, 0);
-  if (r <= 0) {
-    ostringstream oss;
-    oss << "ioctx.read(" << obj << ", bl, 0, 0) returned " << r;
-    return oss.str();
-  }
-  auto p = bl.cbegin();
-  bufferlist header;
-  map<string, bufferlist> m;
-  decode(header, p);
-  decode(m, p);
-  map<string, bufferlist>::iterator i = m.find(key);
-  if (i == m.end())
-    return "";
-  std::string retstring;
-  decode(retstring, i->second);
-  return retstring;
-}
-
-static std::string add_key_to_tmap(IoCtx &ioctx, const std::string &obj,
-         const std::string &key, const std::string &val)
-{
-  __u8 c = CEPH_OSD_TMAP_SET;
-
-  bufferlist tmbl;
-  encode(c, tmbl);
-  encode(key, tmbl);
-  bufferlist blbl;
-  encode(val, blbl);
-  encode(blbl, tmbl);
-  int ret = ioctx.tmap_update(obj, tmbl);
-  if (ret) {
-    ostringstream oss;
-    oss << "ioctx.tmap_update(obj=" << obj << ", key="
-       << key << ", val=" << val << ") failed with error " << ret;
-    return oss.str();
-  }
-  return "";
-}
-
-static int remove_key_from_tmap(IoCtx &ioctx, const std::string &obj,
-                                       const std::string &key)
-{
-  __u8 c = CEPH_OSD_TMAP_RM;
-
-  bufferlist tmbl;
-  encode(c, tmbl);
-  encode(key, tmbl);
-  int ret = ioctx.tmap_update(obj, tmbl);
-  if (ret) {
-    ostringstream oss;
-    oss << "ioctx.tmap_update(obj=" << obj << ", key="
-       << key << ") failed with error " << ret;
-  }
-  return ret;
-}
-
-TEST_F(LibRadosMiscPP, TmapUpdatePP) {
-  // create tmap
-  {
-    __u8 c = CEPH_OSD_TMAP_CREATE;
-    std::string my_tmap("my_tmap");
-    bufferlist emptybl;
-
-    bufferlist tmbl;
-    encode(c, tmbl);
-    encode(my_tmap, tmbl);
-    encode(emptybl, tmbl);
-    ASSERT_EQ(0, ioctx.tmap_update("foo", tmbl));
-  }
-
-  ASSERT_EQ(string(""), add_key_to_tmap(ioctx, "foo", "key1", "val1"));
-
-  ASSERT_EQ(string(""), add_key_to_tmap(ioctx, "foo", "key2", "val2"));
-
-  // read key1 from the tmap
-  ASSERT_EQ(string("val1"), read_key_from_tmap(ioctx, "foo", "key1"));
-
-  // remove key1 from tmap
-  ASSERT_EQ(0, remove_key_from_tmap(ioctx, "foo", "key1"));
-  ASSERT_EQ(-ENOENT, remove_key_from_tmap(ioctx, "foo", "key1"));
-
-  // key should be removed
-  ASSERT_EQ(string(""), read_key_from_tmap(ioctx, "foo", "key1"));
-}
-
-TEST_F(LibRadosMiscPP, TmapUpdateMisorderedPP) {
-  // create tmap
-  {
-    __u8 c = CEPH_OSD_TMAP_CREATE;
-    std::string my_tmap("my_tmap");
-    bufferlist emptybl;
-
-    bufferlist tmbl;
-    encode(c, tmbl);
-    encode(my_tmap, tmbl);
-    encode(emptybl, tmbl);
-    ASSERT_EQ(0, ioctx.tmap_update("foo", tmbl));
-  }
-
-  // good update
-  {
-    __u8 c = CEPH_OSD_TMAP_SET;
-    bufferlist tmbl;
-    encode(c, tmbl);
-    encode("a", tmbl);
-    bufferlist blbl;
-    encode("old", blbl);
-    encode(blbl, tmbl);
-
-    encode(c, tmbl);
-    encode("b", tmbl);
-    encode(blbl, tmbl);
-
-    encode(c, tmbl);
-    encode("c", tmbl);
-    encode(blbl, tmbl);
-
-    ASSERT_EQ(0, ioctx.tmap_update("foo", tmbl));
-  }
-
-  // bad update
-  {
-    __u8 c = CEPH_OSD_TMAP_SET;
-    bufferlist tmbl;
-    encode(c, tmbl);
-    encode("b", tmbl);
-    bufferlist blbl;
-    encode("new", blbl);
-    encode(blbl, tmbl);
-
-    encode(c, tmbl);
-    encode("a", tmbl);
-    encode(blbl, tmbl);
-
-    encode(c, tmbl);
-    encode("c", tmbl);
-    encode(blbl, tmbl);
-
-    ASSERT_EQ(0, ioctx.tmap_update("foo", tmbl));
-  }
-
-  // check
-  ASSERT_EQ(string("new"), read_key_from_tmap(ioctx, "foo", "a"));
-  ASSERT_EQ(string("new"), read_key_from_tmap(ioctx, "foo", "b"));
-  ASSERT_EQ(string("new"), read_key_from_tmap(ioctx, "foo", "c"));
-
-  ASSERT_EQ(0, remove_key_from_tmap(ioctx, "foo", "a"));
-  ASSERT_EQ(string(""), read_key_from_tmap(ioctx, "foo", "a"));
-
-  ASSERT_EQ(0, remove_key_from_tmap(ioctx, "foo", "b"));
-  ASSERT_EQ(string(""), read_key_from_tmap(ioctx, "foo", "a"));
-}
-
-TEST_F(LibRadosMiscPP, TmapUpdateMisorderedPutPP) {
-  // create unsorted tmap
-  string h("header");
-  bufferlist bl;
-  encode(h, bl);
-  uint32_t n = 3;
-  encode(n, bl);
-  encode(string("b"), bl);
-  encode(string("bval"), bl);
-  encode(string("a"), bl);
-  encode(string("aval"), bl);
-  encode(string("c"), bl);
-  encode(string("cval"), bl);
-  bufferlist orig = bl;  // tmap_put steals bl content
-  ASSERT_EQ(0, ioctx.tmap_put("foo", bl));
-
-  // check
-  bufferlist newbl;
-  ioctx.read("foo", newbl, orig.length(), 0);
-  ASSERT_EQ(orig.contents_equal(newbl), false);
-}
-
-TEST_F(LibRadosMiscPP, Tmap2OmapPP) {
-  // create tmap
-  bufferlist hdr;
-  hdr.append("header");
-  map<string, bufferlist> omap;
-  omap["1"].append("a");
-  omap["2"].append("b");
-  omap["3"].append("c");
-  {
-    bufferlist bl;
-    encode(hdr, bl);
-    encode(omap, bl);
-    ASSERT_EQ(0, ioctx.tmap_put("foo", bl));
-  }
-
-  // convert tmap to omap
-  ASSERT_EQ(0, ioctx.tmap_to_omap("foo", false));
-
-  // if tmap was truncated ?
-  {
-    uint64_t size;
-    time_t mtime;
-    ASSERT_EQ(0, ioctx.stat("foo", &size, &mtime));
-    ASSERT_EQ(0U, size);
-  }
-
-  // if 'nullok' works
-  ASSERT_EQ(0, ioctx.tmap_to_omap("foo", true));
-  ASSERT_LE(ioctx.tmap_to_omap("foo", false), 0);
-
-  {
-    // read omap
-    bufferlist got;
-    map<string, bufferlist> m;
-    ObjectReadOperation o;
-    o.omap_get_header(&got, NULL);
-    o.omap_get_vals2("", 1024, &m, nullptr, nullptr);
-    ASSERT_EQ(0, ioctx.operate("foo", &o, NULL));
-
-    // compare header
-    ASSERT_TRUE(hdr.contents_equal(got));
-
-    // compare values
-    ASSERT_EQ(omap.size(), m.size());
-    bool same = true;
-    for (map<string, bufferlist>::iterator p = omap.begin(); p != omap.end(); ++p) {
-      map<string, bufferlist>::iterator q = m.find(p->first);
-      if (q == m.end() || !p->second.contents_equal(q->second)) {
-       same = false;
-       break;
-      }
-    }
-    ASSERT_TRUE(same);
-  }
-}
-
 TEST_F(LibRadosMisc, Exec) {
   char buf[128];
   memset(buf, 0xcc, sizeof(buf));
@@ -522,535 +171,6 @@ TEST_F(LibRadosMisc, Exec) {
   ASSERT_NE(all_features, (unsigned)0);
 }
 
-TEST_F(LibRadosMiscPP, ExecPP) {
-  bufferlist bl;
-  ASSERT_EQ(0, ioctx.write("foo", bl, 0, 0));
-  bufferlist bl2, out;
-  int r = ioctx.exec("foo", "rbd", "get_all_features", bl2, out);
-  ASSERT_EQ(0, r);
-  auto iter = out.cbegin();
-  uint64_t all_features;
-  decode(all_features, iter);
-  // make sure *some* features are specified; don't care which ones
-  ASSERT_NE(all_features, (unsigned)0);
-}
-
-void set_completion_complete(rados_completion_t cb, void *arg)
-{
-  bool *my_aio_complete = (bool*)arg;
-  *my_aio_complete = true;
-}
-
-TEST_F(LibRadosMiscPP, BadFlagsPP) {
-  unsigned badflags = CEPH_OSD_FLAG_PARALLELEXEC;
-  {
-    bufferlist bl;
-    bl.append("data");
-    ASSERT_EQ(0, ioctx.write("badfoo", bl, bl.length(), 0));
-  }
-  {
-    ASSERT_EQ(-EINVAL, ioctx.remove("badfoo", badflags));
-  }
-}
-
-TEST_F(LibRadosMiscPP, Operate1PP) {
-  ObjectWriteOperation o;
-  {
-    bufferlist bl;
-    o.write(0, bl);
-  }
-  std::string val1("val1");
-  {
-    bufferlist bl;
-    bl.append(val1.c_str(), val1.size() + 1);
-    o.setxattr("key1", bl);
-    o.omap_clear(); // shouldn't affect attrs!
-  }
-  ASSERT_EQ(0, ioctx.operate("foo", &o));
-
-  ObjectWriteOperation empty;
-  ASSERT_EQ(0, ioctx.operate("foo", &empty));
-
-  {
-    bufferlist bl;
-    ASSERT_GT(ioctx.getxattr("foo", "key1", bl), 0);
-    ASSERT_EQ(0, strcmp(bl.c_str(), val1.c_str()));
-  }
-  ObjectWriteOperation o2;
-  {
-    bufferlist bl;
-    bl.append(val1);
-    o2.cmpxattr("key1", CEPH_OSD_CMPXATTR_OP_EQ, bl);
-    o2.rmxattr("key1");
-  }
-  ASSERT_EQ(-ECANCELED, ioctx.operate("foo", &o2));
-  ObjectWriteOperation o3;
-  {
-    bufferlist bl;
-    bl.append(val1);
-    o3.cmpxattr("key1", CEPH_OSD_CMPXATTR_OP_EQ, bl);
-  }
-  ASSERT_EQ(-ECANCELED, ioctx.operate("foo", &o3));
-}
-
-TEST_F(LibRadosMiscPP, Operate2PP) {
-  ObjectWriteOperation o;
-  {
-    bufferlist bl;
-    bl.append("abcdefg");
-    o.write(0, bl);
-  }
-  std::string val1("val1");
-  {
-    bufferlist bl;
-    bl.append(val1.c_str(), val1.size() + 1);
-    o.setxattr("key1", bl);
-    o.truncate(0);
-  }
-  ASSERT_EQ(0, ioctx.operate("foo", &o));
-  uint64_t size;
-  time_t mtime;
-  ASSERT_EQ(0, ioctx.stat("foo", &size, &mtime));
-  ASSERT_EQ(0U, size);
-}
-
-TEST_F(LibRadosMiscPP, BigObjectPP) {
-  bufferlist bl;
-  bl.append("abcdefg");
-  ASSERT_EQ(0, ioctx.write("foo", bl, bl.length(), 0));
-
-  {
-    ObjectWriteOperation o;
-    o.truncate(500000000000ull);
-    ASSERT_EQ(-EFBIG, ioctx.operate("foo", &o));
-  }
-  {
-    ObjectWriteOperation o;
-    o.zero(500000000000ull, 1);
-    ASSERT_EQ(-EFBIG, ioctx.operate("foo", &o));
-  }
-  {
-    ObjectWriteOperation o;
-    o.zero(1, 500000000000ull);
-    ASSERT_EQ(-EFBIG, ioctx.operate("foo", &o));
-  }
-  {
-    ObjectWriteOperation o;
-    o.zero(500000000000ull, 500000000000ull);
-    ASSERT_EQ(-EFBIG, ioctx.operate("foo", &o));
-  }
-
-#ifdef __LP64__
-  // this test only works on 64-bit platforms
-  ASSERT_EQ(-EFBIG, ioctx.write("foo", bl, bl.length(), 500000000000ull));
-#endif
-}
-
-TEST_F(LibRadosMiscPP, AioOperatePP) {
-  bool my_aio_complete = false;
-  AioCompletion *my_completion = cluster.aio_create_completion(
-         (void*)&my_aio_complete, set_completion_complete, NULL);
-  AioCompletion *my_completion_null = NULL;
-  ASSERT_NE(my_completion, my_completion_null);
-
-  ObjectWriteOperation o;
-  {
-    bufferlist bl;
-    o.write(0, bl);
-  }
-  std::string val1("val1");
-  {
-    bufferlist bl;
-    bl.append(val1.c_str(), val1.size() + 1);
-    o.setxattr("key1", bl);
-    bufferlist bl2;
-    char buf2[1024];
-    memset(buf2, 0xdd, sizeof(buf2));
-    bl2.append(buf2, sizeof(buf2));
-    o.append(bl2);
-  }
-  ASSERT_EQ(0, ioctx.aio_operate("foo", my_completion, &o));
-  ASSERT_EQ(0, my_completion->wait_for_complete_and_cb());
-  ASSERT_EQ(my_aio_complete, true);
-  my_completion->release();
-
-  uint64_t size;
-  time_t mtime;
-  ASSERT_EQ(0, ioctx.stat("foo", &size, &mtime));
-  ASSERT_EQ(1024U, size);
-}
-
-TEST_F(LibRadosMiscPP, AssertExistsPP) {
-  char buf[64];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl;
-  bl.append(buf, sizeof(buf));
-
-  ObjectWriteOperation op;
-  op.assert_exists();
-  op.write(0, bl);
-  ASSERT_EQ(-ENOENT, ioctx.operate("asdffoo", &op));
-  ASSERT_EQ(0, ioctx.create("asdffoo", true));
-  ASSERT_EQ(0, ioctx.operate("asdffoo", &op));
-  ASSERT_EQ(-EEXIST, ioctx.create("asdffoo", true));
-}
-
-TEST_F(LibRadosMiscPP, AssertVersionPP) {
-  char buf[64];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl;
-  bl.append(buf, sizeof(buf));
-
-  // Create test object...
-  ASSERT_EQ(0, ioctx.create("asdfbar", true));
-  // ...then write it again to guarantee that the
-  // (unsigned) version must be at least 1 (not 0)
-  // since we want to decrement it by 1 later.
-  ASSERT_EQ(0, ioctx.write_full("asdfbar", bl));
-
-  uint64_t v = ioctx.get_last_version();
-  ObjectWriteOperation op1;
-  op1.assert_version(v+1);
-  op1.write(0, bl);
-  ASSERT_EQ(-EOVERFLOW, ioctx.operate("asdfbar", &op1));
-  ObjectWriteOperation op2;
-  op2.assert_version(v-1);
-  op2.write(0, bl);
-  ASSERT_EQ(-ERANGE, ioctx.operate("asdfbar", &op2));
-  ObjectWriteOperation op3;
-  op3.assert_version(v);
-  op3.write(0, bl);
-  ASSERT_EQ(0, ioctx.operate("asdfbar", &op3));
-}
-
-TEST_F(LibRadosMiscPP, BigAttrPP) {
-  char buf[64];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl;
-  bl.append(buf, sizeof(buf));
-
-  ASSERT_EQ(0, ioctx.create("foo", true));
-
-  bufferlist got;
-
-  cout << "osd_max_attr_size = " << g_conf()->osd_max_attr_size << std::endl;
-  if (g_conf()->osd_max_attr_size) {
-    bl.clear();
-    got.clear();
-    bl.append(buffer::create(g_conf()->osd_max_attr_size));
-    ASSERT_EQ(0, ioctx.setxattr("foo", "one", bl));
-    ASSERT_EQ((int)bl.length(), ioctx.getxattr("foo", "one", got));
-    ASSERT_TRUE(bl.contents_equal(got));
-
-    bl.clear();
-    bl.append(buffer::create(g_conf()->osd_max_attr_size+1));
-    ASSERT_EQ(-EFBIG, ioctx.setxattr("foo", "one", bl));
-  } else {
-    cout << "osd_max_attr_size == 0; skipping test" << std::endl;
-  }
-
-  for (int i=0; i<1000; i++) {
-    bl.clear();
-    got.clear();
-    bl.append(buffer::create(std::min<uint64_t>(g_conf()->osd_max_attr_size,
-                                               1024)));
-    char n[10];
-    snprintf(n, sizeof(n), "a%d", i);
-    ASSERT_EQ(0, ioctx.setxattr("foo", n, bl));
-    ASSERT_EQ((int)bl.length(), ioctx.getxattr("foo", n, got));
-    ASSERT_TRUE(bl.contents_equal(got));
-  }
-}
-
-TEST_F(LibRadosMiscPP, CopyPP) {
-  bufferlist bl, x;
-  bl.append("hi there");
-  x.append("bar");
-
-  // small object
-  bufferlist blc = bl;
-  bufferlist xc = x;
-  ASSERT_EQ(0, ioctx.write_full("foo", blc));
-  ASSERT_EQ(0, ioctx.setxattr("foo", "myattr", xc));
-
-  version_t uv = ioctx.get_last_version();
-  {
-    // pass future version
-    ObjectWriteOperation op;
-    op.copy_from2("foo", ioctx, uv + 1, LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
-    ASSERT_EQ(-EOVERFLOW, ioctx.operate("foo.copy", &op));
-  }
-  {
-    // pass old version
-    ObjectWriteOperation op;
-    op.copy_from2("foo", ioctx, uv - 1, LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
-    ASSERT_EQ(-ERANGE, ioctx.operate("foo.copy", &op));
-  }
-  {
-    ObjectWriteOperation op;
-    op.copy_from2("foo", ioctx, uv, LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
-    ASSERT_EQ(0, ioctx.operate("foo.copy", &op));
-
-    bufferlist bl2, x2;
-    ASSERT_EQ((int)bl.length(), ioctx.read("foo.copy", bl2, 10000, 0));
-    ASSERT_TRUE(bl.contents_equal(bl2));
-    ASSERT_EQ((int)x.length(), ioctx.getxattr("foo.copy", "myattr", x2));
-    ASSERT_TRUE(x.contents_equal(x2));
-  }
-
-  // small object without a version
-  {
-    ObjectWriteOperation op;
-    op.copy_from2("foo", ioctx, 0, LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
-    ASSERT_EQ(0, ioctx.operate("foo.copy2", &op));
-
-    bufferlist bl2, x2;
-    ASSERT_EQ((int)bl.length(), ioctx.read("foo.copy2", bl2, 10000, 0));
-    ASSERT_TRUE(bl.contents_equal(bl2));
-    ASSERT_EQ((int)x.length(), ioctx.getxattr("foo.copy2", "myattr", x2));
-    ASSERT_TRUE(x.contents_equal(x2));
-  }
-
-  // do a big object
-  bl.append(buffer::create(g_conf()->osd_copyfrom_max_chunk * 3));
-  bl.zero();
-  bl.append("tail");
-  blc = bl;
-  xc = x;
-  ASSERT_EQ(0, ioctx.write_full("big", blc));
-  ASSERT_EQ(0, ioctx.setxattr("big", "myattr", xc));
-
-  {
-    ObjectWriteOperation op;
-    op.copy_from2("big", ioctx, ioctx.get_last_version(), LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
-    ASSERT_EQ(0, ioctx.operate("big.copy", &op));
-
-    bufferlist bl2, x2;
-    ASSERT_EQ((int)bl.length(), ioctx.read("big.copy", bl2, bl.length(), 0));
-    ASSERT_TRUE(bl.contents_equal(bl2));
-    ASSERT_EQ((int)x.length(), ioctx.getxattr("foo.copy", "myattr", x2));
-    ASSERT_TRUE(x.contents_equal(x2));
-  }
-
-  {
-    ObjectWriteOperation op;
-    op.copy_from2("big", ioctx, 0, LIBRADOS_OP_FLAG_FADVISE_SEQUENTIAL);
-    ASSERT_EQ(0, ioctx.operate("big.copy2", &op));
-
-    bufferlist bl2, x2;
-    ASSERT_EQ((int)bl.length(), ioctx.read("big.copy2", bl2, bl.length(), 0));
-    ASSERT_TRUE(bl.contents_equal(bl2));
-    ASSERT_EQ((int)x.length(), ioctx.getxattr("foo.copy2", "myattr", x2));
-    ASSERT_TRUE(x.contents_equal(x2));
-  }
-}
-
-class LibRadosTwoPoolsECPP : public RadosTestECPP
-{
-public:
-  LibRadosTwoPoolsECPP() {};
-  ~LibRadosTwoPoolsECPP() override {};
-protected:
-  static void SetUpTestCase() {
-    pool_name = get_temp_pool_name();
-    ASSERT_EQ("", create_one_ec_pool_pp(pool_name, s_cluster));
-    src_pool_name = get_temp_pool_name();
-    ASSERT_EQ(0, s_cluster.pool_create(src_pool_name.c_str()));
-
-    librados::IoCtx ioctx;
-    ASSERT_EQ(0, s_cluster.ioctx_create(pool_name.c_str(), ioctx));
-    ioctx.application_enable("rados", true);
-
-    librados::IoCtx src_ioctx;
-    ASSERT_EQ(0, s_cluster.ioctx_create(src_pool_name.c_str(), src_ioctx));
-    src_ioctx.application_enable("rados", true);
-  }
-  static void TearDownTestCase() {
-    ASSERT_EQ(0, s_cluster.pool_delete(src_pool_name.c_str()));
-    ASSERT_EQ(0, destroy_one_ec_pool_pp(pool_name, s_cluster));
-  }
-  static std::string src_pool_name;
-
-  void SetUp() override {
-    RadosTestECPP::SetUp();
-    ASSERT_EQ(0, cluster.ioctx_create(src_pool_name.c_str(), src_ioctx));
-    src_ioctx.set_namespace(nspace);
-  }
-  void TearDown() override {
-    // wait for maps to settle before next test
-    cluster.wait_for_latest_osdmap();
-
-    RadosTestECPP::TearDown();
-
-    cleanup_default_namespace(src_ioctx);
-    cleanup_namespace(src_ioctx, nspace);
-
-    src_ioctx.close();
-  }
-
-  librados::IoCtx src_ioctx;
-};
-std::string LibRadosTwoPoolsECPP::src_pool_name;
-
-//copy_from between ecpool and no-ecpool.
-TEST_F(LibRadosTwoPoolsECPP, CopyFrom) {
-  bufferlist z;
-  z.append_zero(4194304*2);
-  bufferlist b;
-  b.append("copyfrom");
-
-  // create big object w/ omapheader
-  {
-    ASSERT_EQ(0, src_ioctx.write_full("foo", z));
-    ASSERT_EQ(0, src_ioctx.omap_set_header("foo", b));
-    version_t uv = src_ioctx.get_last_version();
-    ObjectWriteOperation op;
-    op.copy_from("foo", src_ioctx, uv);
-    ASSERT_EQ(-EOPNOTSUPP, ioctx.operate("foo.copy", &op));
-  }
-
-  // same with small object
-  {
-    ASSERT_EQ(0, src_ioctx.omap_set_header("bar", b));
-    version_t uv = src_ioctx.get_last_version();
-    ObjectWriteOperation op;
-    op.copy_from("bar", src_ioctx, uv);
-    ASSERT_EQ(-EOPNOTSUPP, ioctx.operate("bar.copy", &op));
-  }
-}
-
-TEST_F(LibRadosMiscPP, CopyScrubPP) {
-  bufferlist inbl, bl, x;
-  for (int i=0; i<100; ++i)
-    x.append("barrrrrrrrrrrrrrrrrrrrrrrrrr");
-  bl.append(buffer::create(g_conf()->osd_copyfrom_max_chunk * 3));
-  bl.zero();
-  bl.append("tail");
-  bufferlist cbl;
-
-  map<string, bufferlist> to_set;
-  for (int i=0; i<1000; ++i)
-    to_set[string("foo") + stringify(i)] = x;
-
-  // small
-  cbl = x;
-  ASSERT_EQ(0, ioctx.write_full("small", cbl));
-  ASSERT_EQ(0, ioctx.setxattr("small", "myattr", x));
-
-  // big
-  cbl = bl;
-  ASSERT_EQ(0, ioctx.write_full("big", cbl));
-
-  // without header
-  cbl = bl;
-  ASSERT_EQ(0, ioctx.write_full("big2", cbl));
-  ASSERT_EQ(0, ioctx.setxattr("big2", "myattr", x));
-  ASSERT_EQ(0, ioctx.setxattr("big2", "myattr2", x));
-  ASSERT_EQ(0, ioctx.omap_set("big2", to_set));
-
-  // with header
-  cbl = bl;
-  ASSERT_EQ(0, ioctx.write_full("big3", cbl));
-  ASSERT_EQ(0, ioctx.omap_set_header("big3", x));
-  ASSERT_EQ(0, ioctx.omap_set("big3", to_set));
-
-  // deep scrub to ensure digests are in place
-  {
-    for (int i=0; i<10; ++i) {
-      ostringstream ss;
-      ss << "{\"prefix\": \"pg deep-scrub\", \"pgid\": \""
-        << ioctx.get_id() << "." << i
-        << "\"}";
-      cluster.mon_command(ss.str(), inbl, NULL, NULL);
-    }
-
-    // give it a few seconds to go.  this is sloppy but is usually enough time
-    cout << "waiting for initial deep scrubs..." << std::endl;
-    sleep(30);
-    cout << "done waiting, doing copies" << std::endl;
-  }
-
-  {
-    ObjectWriteOperation op;
-    op.copy_from("small", ioctx, 0);
-    ASSERT_EQ(0, ioctx.operate("small.copy", &op));
-  }
-
-  {
-    ObjectWriteOperation op;
-    op.copy_from("big", ioctx, 0);
-    ASSERT_EQ(0, ioctx.operate("big.copy", &op));
-  }
-
-  {
-    ObjectWriteOperation op;
-    op.copy_from("big2", ioctx, 0);
-    ASSERT_EQ(0, ioctx.operate("big2.copy", &op));
-  }
-
-  {
-    ObjectWriteOperation op;
-    op.copy_from("big3", ioctx, 0);
-    ASSERT_EQ(0, ioctx.operate("big3.copy", &op));
-  }
-
-  // deep scrub to ensure digests are correct
-  {
-    for (int i=0; i<10; ++i) {
-      ostringstream ss;
-      ss << "{\"prefix\": \"pg deep-scrub\", \"pgid\": \""
-        << ioctx.get_id() << "." << i
-        << "\"}";
-      cluster.mon_command(ss.str(), inbl, NULL, NULL);
-    }
-
-    // give it a few seconds to go.  this is sloppy but is usually enough time
-    cout << "waiting for final deep scrubs..." << std::endl;
-    sleep(30);
-    cout << "done waiting" << std::endl;
-  }
-}
-
-TEST_F(LibRadosMiscPP, WriteSamePP) {
-  bufferlist bl;
-  char buf[128];
-  bufferlist fl;
-  char full[128 * 4];
-  char *cmp;
-
-  /* zero the full range before using writesame */
-  memset(full, 0, sizeof(full));
-  fl.append(full, sizeof(full));
-  ASSERT_EQ(0, ioctx.write("ws", fl, fl.length(), 0));
-
-  memset(buf, 0xcc, sizeof(buf));
-  bl.clear();
-  bl.append(buf, sizeof(buf));
-  /* write the same buf four times */
-  ASSERT_EQ(0, ioctx.writesame("ws", bl, sizeof(full), 0));
-
-  /* read back the full buffer and confirm that it matches */
-  fl.clear();
-  fl.append(full, sizeof(full));
-  ASSERT_EQ((int)fl.length(), ioctx.read("ws", fl, fl.length(), 0));
-
-  for (cmp = fl.c_str(); cmp < fl.c_str() + fl.length(); cmp += sizeof(buf)) {
-    ASSERT_EQ(0, memcmp(cmp, buf, sizeof(buf)));
-  }
-
-  /* write_len not a multiple of data_len should throw error */
-  bl.clear();
-  bl.append(buf, sizeof(buf));
-  ASSERT_EQ(-EINVAL, ioctx.writesame("ws", bl, (sizeof(buf) * 4) - 1, 0));
-  ASSERT_EQ(-EINVAL,
-           ioctx.writesame("ws", bl, bl.length() / 2, 0));
-  /* write_len = data_len, i.e. same as write() */
-  ASSERT_EQ(0, ioctx.writesame("ws", bl, sizeof(buf), 0));
-  bl.clear();
-  ASSERT_EQ(-EINVAL,
-           ioctx.writesame("ws", bl, sizeof(buf), 0));
-}
-
 TEST_F(LibRadosMisc, WriteSame) {
   char buf[128];
   char full[128 * 4];
@@ -1082,147 +202,6 @@ TEST_F(LibRadosMisc, WriteSame) {
   ASSERT_EQ(0, rados_writesame(ioctx, "ws", buf, sizeof(buf), sizeof(buf), 0));
 }
 
-template <typename T>
-class LibRadosChecksum : public LibRadosMiscPP {
-public:
-  typedef typename T::alg_t alg_t;
-  typedef typename T::value_t value_t;
-  typedef typename alg_t::init_value_t init_value_t;
-
-  static const rados_checksum_type_t type = T::type;
-
-  bufferlist content_bl;
-
-  using LibRadosMiscPP::SetUpTestCase;
-  using LibRadosMiscPP::TearDownTestCase;
-
-  void SetUp() override {
-    LibRadosMiscPP::SetUp();
-
-    std::string content(4096, '\0');
-    for (size_t i = 0; i < content.length(); ++i) {
-      content[i] = static_cast<char>(rand() % (126 - 33) + 33);
-    }
-    content_bl.append(content);
-    ASSERT_EQ(0, ioctx.write("foo", content_bl, content_bl.length(), 0));
-  }
-};
-
-template <rados_checksum_type_t _type, typename AlgT, typename ValueT>
-class LibRadosChecksumParams {
-public:
-  typedef AlgT alg_t;
-  typedef ValueT value_t;
-  static const rados_checksum_type_t type = _type;
-};
-
-typedef ::testing::Types<
-    LibRadosChecksumParams<LIBRADOS_CHECKSUM_TYPE_XXHASH32,
-                          Checksummer::xxhash32, uint32_t>,
-    LibRadosChecksumParams<LIBRADOS_CHECKSUM_TYPE_XXHASH64,
-                          Checksummer::xxhash64, uint64_t>,
-    LibRadosChecksumParams<LIBRADOS_CHECKSUM_TYPE_CRC32C,
-                          Checksummer::crc32c, uint32_t>
-  > LibRadosChecksumTypes;
-
-TYPED_TEST_CASE(LibRadosChecksum, LibRadosChecksumTypes);
-
-TYPED_TEST(LibRadosChecksum, Subset) {
-  uint32_t chunk_size = 1024;
-  uint32_t csum_count = this->content_bl.length() / chunk_size;
-
-  typename TestFixture::init_value_t init_value = -1;
-  bufferlist init_value_bl;
-  encode(init_value, init_value_bl);
-
-  std::vector<bufferlist> checksum_bls(csum_count);
-  std::vector<int> checksum_rvals(csum_count);
-
-  // individual checksum ops for each chunk
-  ObjectReadOperation op;
-  for (uint32_t i = 0; i < csum_count; ++i) {
-    op.checksum(TestFixture::type, init_value_bl, i * chunk_size, chunk_size,
-               0, &checksum_bls[i], &checksum_rvals[i]);
-  }
-  ASSERT_EQ(0, this->ioctx.operate("foo", &op, NULL));
-
-  for (uint32_t i = 0; i < csum_count; ++i) {
-    ASSERT_EQ(0, checksum_rvals[i]);
-
-    auto bl_it = checksum_bls[i].cbegin();
-    uint32_t count;
-    decode(count, bl_it);
-    ASSERT_EQ(1U, count);
-
-    typename TestFixture::value_t value;
-    decode(value, bl_it);
-
-    bufferlist content_sub_bl;
-    content_sub_bl.substr_of(this->content_bl, i * chunk_size, chunk_size);
-
-    typename TestFixture::value_t expected_value;
-    bufferptr expected_value_bp = buffer::create_static(
-      sizeof(expected_value), reinterpret_cast<char*>(&expected_value));
-    Checksummer::template calculate<typename TestFixture::alg_t>(
-      init_value, chunk_size, 0, chunk_size, content_sub_bl,
-      &expected_value_bp);
-    ASSERT_EQ(expected_value, value);
-  }
-}
-
-TYPED_TEST(LibRadosChecksum, Chunked) {
-  uint32_t chunk_size = 1024;
-  uint32_t csum_count = this->content_bl.length() / chunk_size;
-
-  typename TestFixture::init_value_t init_value = -1;
-  bufferlist init_value_bl;
-  encode(init_value, init_value_bl);
-
-  bufferlist checksum_bl;
-  int checksum_rval;
-
-  // single op with chunked checksum results
-  ObjectReadOperation op;
-  op.checksum(TestFixture::type, init_value_bl, 0, this->content_bl.length(),
-             chunk_size, &checksum_bl, &checksum_rval);
-  ASSERT_EQ(0, this->ioctx.operate("foo", &op, NULL));
-  ASSERT_EQ(0, checksum_rval);
-
-  auto bl_it = checksum_bl.cbegin();
-  uint32_t count;
-  decode(count, bl_it);
-  ASSERT_EQ(csum_count, count);
-
-  std::vector<typename TestFixture::value_t> expected_values(csum_count);
-  bufferptr expected_values_bp = buffer::create_static(
-    csum_count * sizeof(typename TestFixture::value_t),
-    reinterpret_cast<char*>(&expected_values[0]));
-
-  Checksummer::template calculate<typename TestFixture::alg_t>(
-    init_value, chunk_size, 0, this->content_bl.length(), this->content_bl,
-    &expected_values_bp);
-
-  for (uint32_t i = 0; i < csum_count; ++i) {
-    typename TestFixture::value_t value;
-    decode(value, bl_it);
-    ASSERT_EQ(expected_values[i], value);
-  }
-}
-
-TEST_F(LibRadosMiscPP, CmpExtPP) {
-  bufferlist cmp_bl, bad_cmp_bl, write_bl;
-  char stored_str[] = "1234567891";
-  char mismatch_str[] = "1234577777";
-
-  write_bl.append(stored_str);
-  ioctx.write("cmpextpp", write_bl, write_bl.length(), 0);
-  cmp_bl.append(stored_str);
-  ASSERT_EQ(0, ioctx.cmpext("cmpextpp", 0, cmp_bl));
-
-  bad_cmp_bl.append(mismatch_str);
-  ASSERT_EQ(-MAX_ERRNO - 5, ioctx.cmpext("cmpextpp", 0, bad_cmp_bl));
-}
-
 TEST_F(LibRadosMisc, CmpExt) {
   bufferlist cmp_bl, bad_cmp_bl, write_bl;
   char stored_str[] = "1234567891";
@@ -1310,74 +289,6 @@ TEST_F(LibRadosMisc, Applications) {
   ASSERT_EQ(0, memcmp("value2\0", vals, val_len));
 }
 
-TEST_F(LibRadosMiscPP, Applications) {
-  bufferlist inbl, outbl;
-  string outs;
-  ASSERT_EQ(0, cluster.mon_command("{\"prefix\": \"osd dump\"}",
-                                  inbl, &outbl, &outs));
-  ASSERT_LT(0u, outbl.length());
-  ASSERT_LE(0u, outs.length());
-  if (!std::regex_search(outbl.to_str(),
-                        std::regex("require_osd_release [l-z]"))) {
-    std::cout << "SKIPPING";
-    return;
-  }
-
-  std::set<std::string> expected_apps = {"rados"};
-  std::set<std::string> apps;
-  ASSERT_EQ(0, ioctx.application_list(&apps));
-  ASSERT_EQ(expected_apps, apps);
-
-  ASSERT_EQ(0, ioctx.application_enable("app1", true));
-  ASSERT_EQ(-EPERM, ioctx.application_enable("app2", false));
-  ASSERT_EQ(0, ioctx.application_enable("app2", true));
-
-  expected_apps = {"app1", "app2", "rados"};
-  ASSERT_EQ(0, ioctx.application_list(&apps));
-  ASSERT_EQ(expected_apps, apps);
-
-  std::map<std::string, std::string> expected_meta;
-  std::map<std::string, std::string> meta;
-  ASSERT_EQ(-ENOENT, ioctx.application_metadata_list("dne", &meta));
-  ASSERT_EQ(0, ioctx.application_metadata_list("app1", &meta));
-  ASSERT_EQ(expected_meta, meta);
-
-  ASSERT_EQ(-ENOENT, ioctx.application_metadata_set("dne", "key1", "value1"));
-  ASSERT_EQ(0, ioctx.application_metadata_set("app1", "key1", "value1"));
-  ASSERT_EQ(0, ioctx.application_metadata_set("app1", "key2", "value2"));
-
-  expected_meta = {{"key1", "value1"}, {"key2", "value2"}};
-  ASSERT_EQ(0, ioctx.application_metadata_list("app1", &meta));
-  ASSERT_EQ(expected_meta, meta);
-
-  ASSERT_EQ(0, ioctx.application_metadata_remove("app1", "key1"));
-
-  expected_meta = {{"key2", "value2"}};
-  ASSERT_EQ(0, ioctx.application_metadata_list("app1", &meta));
-  ASSERT_EQ(expected_meta, meta);
-}
-
-TEST_F(LibRadosMiscECPP, CompareExtentRange) {
-  bufferlist bl1;
-  bl1.append("ceph");
-  ObjectWriteOperation write;
-  write.write(0, bl1);
-  ASSERT_EQ(0, ioctx.operate("foo", &write));
-
-  bufferlist bl2;
-  bl2.append("ph");
-  bl2.append(std::string(2, '\0'));
-  ObjectReadOperation read1;
-  read1.cmpext(2, bl2, nullptr);
-  ASSERT_EQ(0, ioctx.operate("foo", &read1, nullptr));
-
-  bufferlist bl3;
-  bl3.append(std::string(4, '\0'));
-  ObjectReadOperation read2;
-  read2.cmpext(2097152, bl3, nullptr);
-  ASSERT_EQ(0, ioctx.operate("foo", &read2, nullptr));
-}
-
 TEST_F(LibRadosMisc, MinCompatOSD) {
   int8_t require_osd_release;
   ASSERT_EQ(0, rados_get_min_compatible_osd(cluster, &require_osd_release));
@@ -1385,13 +296,6 @@ TEST_F(LibRadosMisc, MinCompatOSD) {
   ASSERT_GT(CEPH_RELEASE_MAX, require_osd_release);
 }
 
-TEST_F(LibRadosMiscPP, MinCompatOSD) {
-  int8_t require_osd_release;
-  ASSERT_EQ(0, cluster.get_min_compatible_osd(&require_osd_release));
-  ASSERT_LE(-1, require_osd_release);
-  ASSERT_GT(CEPH_RELEASE_MAX, require_osd_release);
-}
-
 TEST_F(LibRadosMisc, MinCompatClient) {
   int8_t min_compat_client;
   int8_t require_min_compat_client;
@@ -1404,30 +308,3 @@ TEST_F(LibRadosMisc, MinCompatClient) {
   ASSERT_LE(-1, require_min_compat_client);
   ASSERT_GT(CEPH_RELEASE_MAX, require_min_compat_client);
 }
-
-TEST_F(LibRadosMiscPP, MinCompatClient) {
-  int8_t min_compat_client;
-  int8_t require_min_compat_client;
-  ASSERT_EQ(0, cluster.get_min_compatible_client(&min_compat_client,
-                                                 &require_min_compat_client));
-  ASSERT_LE(-1, min_compat_client);
-  ASSERT_GT(CEPH_RELEASE_MAX, min_compat_client);
-
-  ASSERT_LE(-1, require_min_compat_client);
-  ASSERT_GT(CEPH_RELEASE_MAX, require_min_compat_client);
-}
-
-TEST_F(LibRadosMiscPP, Conf) {
-  const char* const option = "bluestore_throttle_bytes";
-  size_t new_size = 1 << 20;
-  std::string original;
-  ASSERT_EQ(0, cluster.conf_get(option, original));
-  auto restore_setting = make_scope_guard([&] {
-    cluster.conf_set(option, original.c_str());
-  });
-  std::string expected = std::to_string(new_size);
-  ASSERT_EQ(0, cluster.conf_set(option, expected.c_str()));
-  std::string actual;
-  ASSERT_EQ(0, cluster.conf_get(option, actual));
-  ASSERT_EQ(expected, actual);
-}
diff --git a/src/test/librados/misc_cxx.cc b/src/test/librados/misc_cxx.cc
new file mode 100644 (file)
index 0000000..cf11c19
--- /dev/null
@@ -0,0 +1,1151 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#include <errno.h>
+#include <map>
+#include <sstream>
+#include <string>
+#include <regex>
+
+#include "gtest/gtest.h"
+
+#include "include/err.h"
+#include "include/buffer.h"
+#include "include/rbd_types.h"
+#include "include/rados.h"
+#include "include/rados/librados.hpp"
+#include "include/scope_guard.h"
+#include "include/stringify.h"
+#include "common/Checksummer.h"
+#include "mds/mdstypes.h"
+#include "global/global_context.h"
+#include "test/librados/testcase_cxx.h"
+#include "test/librados/test_cxx.h"
+
+using namespace librados;
+using std::map;
+using std::ostringstream;
+using std::string;
+
+typedef RadosTestPP LibRadosMiscPP;
+typedef RadosTestECPP LibRadosMiscECPP;
+
+TEST(LibRadosMiscVersion, VersionPP) {
+  int major, minor, extra;
+  Rados::version(&major, &minor, &extra);
+}
+
+TEST_F(LibRadosMiscPP, WaitOSDMapPP) {
+  ASSERT_EQ(0, cluster.wait_for_latest_osdmap());
+}
+
+TEST_F(LibRadosMiscPP, LongNamePP) {
+  bufferlist bl;
+  bl.append("content");
+  int maxlen = g_conf()->osd_max_object_name_len;
+  ASSERT_EQ(0, ioctx.write(string(maxlen/2, 'a').c_str(), bl, bl.length(), 0));
+  ASSERT_EQ(0, ioctx.write(string(maxlen-1, 'a').c_str(), bl, bl.length(), 0));
+  ASSERT_EQ(0, ioctx.write(string(maxlen, 'a').c_str(), bl, bl.length(), 0));
+  ASSERT_EQ(-ENAMETOOLONG, ioctx.write(string(maxlen+1, 'a').c_str(), bl, bl.length(), 0));
+  ASSERT_EQ(-ENAMETOOLONG, ioctx.write(string(maxlen*2, 'a').c_str(), bl, bl.length(), 0));
+}
+
+TEST_F(LibRadosMiscPP, LongLocatorPP) {
+  bufferlist bl;
+  bl.append("content");
+  int maxlen = g_conf()->osd_max_object_name_len;
+  ioctx.locator_set_key(
+    string((maxlen/2), 'a'));
+  ASSERT_EQ(
+    0,
+    ioctx.write(
+      string("a").c_str(),
+      bl, bl.length(), 0));
+  ioctx.locator_set_key(
+    string(maxlen - 1, 'a'));
+  ASSERT_EQ(
+    0,
+    ioctx.write(
+      string("a").c_str(),
+      bl, bl.length(), 0));
+  ioctx.locator_set_key(
+    string(maxlen, 'a'));
+  ASSERT_EQ(
+    0,
+    ioctx.write(
+      string("a").c_str(),
+      bl, bl.length(), 0));
+  ioctx.locator_set_key(
+    string(maxlen+1, 'a'));
+  ASSERT_EQ(
+    -ENAMETOOLONG,
+    ioctx.write(
+      string("a").c_str(),
+      bl, bl.length(), 0));
+  ioctx.locator_set_key(
+    string((maxlen*2), 'a'));
+  ASSERT_EQ(
+    -ENAMETOOLONG,
+    ioctx.write(
+      string("a").c_str(),
+      bl, bl.length(), 0));
+}
+
+TEST_F(LibRadosMiscPP, LongNSpacePP) {
+  bufferlist bl;
+  bl.append("content");
+  int maxlen = g_conf()->osd_max_object_namespace_len;
+  ioctx.set_namespace(
+    string((maxlen/2), 'a'));
+  ASSERT_EQ(
+    0,
+    ioctx.write(
+      string("a").c_str(),
+      bl, bl.length(), 0));
+  ioctx.set_namespace(
+    string(maxlen - 1, 'a'));
+  ASSERT_EQ(
+    0,
+    ioctx.write(
+      string("a").c_str(),
+      bl, bl.length(), 0));
+  ioctx.set_namespace(
+    string(maxlen, 'a'));
+  ASSERT_EQ(
+    0,
+    ioctx.write(
+      string("a").c_str(),
+      bl, bl.length(), 0));
+  ioctx.set_namespace(
+    string(maxlen+1, 'a'));
+  ASSERT_EQ(
+    -ENAMETOOLONG,
+    ioctx.write(
+      string("a").c_str(),
+      bl, bl.length(), 0));
+  ioctx.set_namespace(
+    string((maxlen*2), 'a'));
+  ASSERT_EQ(
+    -ENAMETOOLONG,
+    ioctx.write(
+      string("a").c_str(),
+      bl, bl.length(), 0));
+}
+
+TEST_F(LibRadosMiscPP, LongAttrNamePP) {
+  bufferlist bl;
+  bl.append("content");
+  int maxlen = g_conf()->osd_max_attr_name_len;
+  ASSERT_EQ(0, ioctx.setxattr("bigattrobj", string(maxlen/2, 'a').c_str(), bl));
+  ASSERT_EQ(0, ioctx.setxattr("bigattrobj", string(maxlen-1, 'a').c_str(), bl));
+  ASSERT_EQ(0, ioctx.setxattr("bigattrobj", string(maxlen, 'a').c_str(), bl));
+  ASSERT_EQ(-ENAMETOOLONG, ioctx.setxattr("bigattrobj", string(maxlen+1, 'a').c_str(), bl));
+  ASSERT_EQ(-ENAMETOOLONG, ioctx.setxattr("bigattrobj", string(maxlen*2, 'a').c_str(), bl));
+}
+
+static std::string read_key_from_tmap(IoCtx& ioctx, const std::string &obj,
+                                     const std::string &key)
+{
+  bufferlist bl;
+  int r = ioctx.read(obj, bl, 0, 0);
+  if (r <= 0) {
+    ostringstream oss;
+    oss << "ioctx.read(" << obj << ", bl, 0, 0) returned " << r;
+    return oss.str();
+  }
+  auto p = bl.cbegin();
+  bufferlist header;
+  map<string, bufferlist> m;
+  decode(header, p);
+  decode(m, p);
+  map<string, bufferlist>::iterator i = m.find(key);
+  if (i == m.end())
+    return "";
+  std::string retstring;
+  decode(retstring, i->second);
+  return retstring;
+}
+
+static std::string add_key_to_tmap(IoCtx &ioctx, const std::string &obj,
+         const std::string &key, const std::string &val)
+{
+  __u8 c = CEPH_OSD_TMAP_SET;
+
+  bufferlist tmbl;
+  encode(c, tmbl);
+  encode(key, tmbl);
+  bufferlist blbl;
+  encode(val, blbl);
+  encode(blbl, tmbl);
+  int ret = ioctx.tmap_update(obj, tmbl);
+  if (ret) {
+    ostringstream oss;
+    oss << "ioctx.tmap_update(obj=" << obj << ", key="
+       << key << ", val=" << val << ") failed with error " << ret;
+    return oss.str();
+  }
+  return "";
+}
+
+static int remove_key_from_tmap(IoCtx &ioctx, const std::string &obj,
+                                       const std::string &key)
+{
+  __u8 c = CEPH_OSD_TMAP_RM;
+
+  bufferlist tmbl;
+  encode(c, tmbl);
+  encode(key, tmbl);
+  int ret = ioctx.tmap_update(obj, tmbl);
+  if (ret) {
+    ostringstream oss;
+    oss << "ioctx.tmap_update(obj=" << obj << ", key="
+       << key << ") failed with error " << ret;
+  }
+  return ret;
+}
+
+TEST_F(LibRadosMiscPP, TmapUpdatePP) {
+  // create tmap
+  {
+    __u8 c = CEPH_OSD_TMAP_CREATE;
+    std::string my_tmap("my_tmap");
+    bufferlist emptybl;
+
+    bufferlist tmbl;
+    encode(c, tmbl);
+    encode(my_tmap, tmbl);
+    encode(emptybl, tmbl);
+    ASSERT_EQ(0, ioctx.tmap_update("foo", tmbl));
+  }
+
+  ASSERT_EQ(string(""), add_key_to_tmap(ioctx, "foo", "key1", "val1"));
+
+  ASSERT_EQ(string(""), add_key_to_tmap(ioctx, "foo", "key2", "val2"));
+
+  // read key1 from the tmap
+  ASSERT_EQ(string("val1"), read_key_from_tmap(ioctx, "foo", "key1"));
+
+  // remove key1 from tmap
+  ASSERT_EQ(0, remove_key_from_tmap(ioctx, "foo", "key1"));
+  ASSERT_EQ(-ENOENT, remove_key_from_tmap(ioctx, "foo", "key1"));
+
+  // key should be removed
+  ASSERT_EQ(string(""), read_key_from_tmap(ioctx, "foo", "key1"));
+}
+
+TEST_F(LibRadosMiscPP, TmapUpdateMisorderedPP) {
+  // create tmap
+  {
+    __u8 c = CEPH_OSD_TMAP_CREATE;
+    std::string my_tmap("my_tmap");
+    bufferlist emptybl;
+
+    bufferlist tmbl;
+    encode(c, tmbl);
+    encode(my_tmap, tmbl);
+    encode(emptybl, tmbl);
+    ASSERT_EQ(0, ioctx.tmap_update("foo", tmbl));
+  }
+
+  // good update
+  {
+    __u8 c = CEPH_OSD_TMAP_SET;
+    bufferlist tmbl;
+    encode(c, tmbl);
+    encode("a", tmbl);
+    bufferlist blbl;
+    encode("old", blbl);
+    encode(blbl, tmbl);
+
+    encode(c, tmbl);
+    encode("b", tmbl);
+    encode(blbl, tmbl);
+
+    encode(c, tmbl);
+    encode("c", tmbl);
+    encode(blbl, tmbl);
+
+    ASSERT_EQ(0, ioctx.tmap_update("foo", tmbl));
+  }
+
+  // bad update
+  {
+    __u8 c = CEPH_OSD_TMAP_SET;
+    bufferlist tmbl;
+    encode(c, tmbl);
+    encode("b", tmbl);
+    bufferlist blbl;
+    encode("new", blbl);
+    encode(blbl, tmbl);
+
+    encode(c, tmbl);
+    encode("a", tmbl);
+    encode(blbl, tmbl);
+
+    encode(c, tmbl);
+    encode("c", tmbl);
+    encode(blbl, tmbl);
+
+    ASSERT_EQ(0, ioctx.tmap_update("foo", tmbl));
+  }
+
+  // check
+  ASSERT_EQ(string("new"), read_key_from_tmap(ioctx, "foo", "a"));
+  ASSERT_EQ(string("new"), read_key_from_tmap(ioctx, "foo", "b"));
+  ASSERT_EQ(string("new"), read_key_from_tmap(ioctx, "foo", "c"));
+
+  ASSERT_EQ(0, remove_key_from_tmap(ioctx, "foo", "a"));
+  ASSERT_EQ(string(""), read_key_from_tmap(ioctx, "foo", "a"));
+
+  ASSERT_EQ(0, remove_key_from_tmap(ioctx, "foo", "b"));
+  ASSERT_EQ(string(""), read_key_from_tmap(ioctx, "foo", "a"));
+}
+
+TEST_F(LibRadosMiscPP, TmapUpdateMisorderedPutPP) {
+  // create unsorted tmap
+  string h("header");
+  bufferlist bl;
+  encode(h, bl);
+  uint32_t n = 3;
+  encode(n, bl);
+  encode(string("b"), bl);
+  encode(string("bval"), bl);
+  encode(string("a"), bl);
+  encode(string("aval"), bl);
+  encode(string("c"), bl);
+  encode(string("cval"), bl);
+  bufferlist orig = bl;  // tmap_put steals bl content
+  ASSERT_EQ(0, ioctx.tmap_put("foo", bl));
+
+  // check
+  bufferlist newbl;
+  ioctx.read("foo", newbl, orig.length(), 0);
+  ASSERT_EQ(orig.contents_equal(newbl), false);
+}
+
+TEST_F(LibRadosMiscPP, Tmap2OmapPP) {
+  // create tmap
+  bufferlist hdr;
+  hdr.append("header");
+  map<string, bufferlist> omap;
+  omap["1"].append("a");
+  omap["2"].append("b");
+  omap["3"].append("c");
+  {
+    bufferlist bl;
+    encode(hdr, bl);
+    encode(omap, bl);
+    ASSERT_EQ(0, ioctx.tmap_put("foo", bl));
+  }
+
+  // convert tmap to omap
+  ASSERT_EQ(0, ioctx.tmap_to_omap("foo", false));
+
+  // if tmap was truncated ?
+  {
+    uint64_t size;
+    time_t mtime;
+    ASSERT_EQ(0, ioctx.stat("foo", &size, &mtime));
+    ASSERT_EQ(0U, size);
+  }
+
+  // if 'nullok' works
+  ASSERT_EQ(0, ioctx.tmap_to_omap("foo", true));
+  ASSERT_LE(ioctx.tmap_to_omap("foo", false), 0);
+
+  {
+    // read omap
+    bufferlist got;
+    map<string, bufferlist> m;
+    ObjectReadOperation o;
+    o.omap_get_header(&got, NULL);
+    o.omap_get_vals2("", 1024, &m, nullptr, nullptr);
+    ASSERT_EQ(0, ioctx.operate("foo", &o, NULL));
+
+    // compare header
+    ASSERT_TRUE(hdr.contents_equal(got));
+
+    // compare values
+    ASSERT_EQ(omap.size(), m.size());
+    bool same = true;
+    for (map<string, bufferlist>::iterator p = omap.begin(); p != omap.end(); ++p) {
+      map<string, bufferlist>::iterator q = m.find(p->first);
+      if (q == m.end() || !p->second.contents_equal(q->second)) {
+       same = false;
+       break;
+      }
+    }
+    ASSERT_TRUE(same);
+  }
+}
+
+TEST_F(LibRadosMiscPP, ExecPP) {
+  bufferlist bl;
+  ASSERT_EQ(0, ioctx.write("foo", bl, 0, 0));
+  bufferlist bl2, out;
+  int r = ioctx.exec("foo", "rbd", "get_all_features", bl2, out);
+  ASSERT_EQ(0, r);
+  auto iter = out.cbegin();
+  uint64_t all_features;
+  decode(all_features, iter);
+  // make sure *some* features are specified; don't care which ones
+  ASSERT_NE(all_features, (unsigned)0);
+}
+
+void set_completion_complete(rados_completion_t cb, void *arg)
+{
+  bool *my_aio_complete = (bool*)arg;
+  *my_aio_complete = true;
+}
+
+TEST_F(LibRadosMiscPP, BadFlagsPP) {
+  unsigned badflags = CEPH_OSD_FLAG_PARALLELEXEC;
+  {
+    bufferlist bl;
+    bl.append("data");
+    ASSERT_EQ(0, ioctx.write("badfoo", bl, bl.length(), 0));
+  }
+  {
+    ASSERT_EQ(-EINVAL, ioctx.remove("badfoo", badflags));
+  }
+}
+
+TEST_F(LibRadosMiscPP, Operate1PP) {
+  ObjectWriteOperation o;
+  {
+    bufferlist bl;
+    o.write(0, bl);
+  }
+  std::string val1("val1");
+  {
+    bufferlist bl;
+    bl.append(val1.c_str(), val1.size() + 1);
+    o.setxattr("key1", bl);
+    o.omap_clear(); // shouldn't affect attrs!
+  }
+  ASSERT_EQ(0, ioctx.operate("foo", &o));
+
+  ObjectWriteOperation empty;
+  ASSERT_EQ(0, ioctx.operate("foo", &empty));
+
+  {
+    bufferlist bl;
+    ASSERT_GT(ioctx.getxattr("foo", "key1", bl), 0);
+    ASSERT_EQ(0, strcmp(bl.c_str(), val1.c_str()));
+  }
+  ObjectWriteOperation o2;
+  {
+    bufferlist bl;
+    bl.append(val1);
+    o2.cmpxattr("key1", CEPH_OSD_CMPXATTR_OP_EQ, bl);
+    o2.rmxattr("key1");
+  }
+  ASSERT_EQ(-ECANCELED, ioctx.operate("foo", &o2));
+  ObjectWriteOperation o3;
+  {
+    bufferlist bl;
+    bl.append(val1);
+    o3.cmpxattr("key1", CEPH_OSD_CMPXATTR_OP_EQ, bl);
+  }
+  ASSERT_EQ(-ECANCELED, ioctx.operate("foo", &o3));
+}
+
+TEST_F(LibRadosMiscPP, Operate2PP) {
+  ObjectWriteOperation o;
+  {
+    bufferlist bl;
+    bl.append("abcdefg");
+    o.write(0, bl);
+  }
+  std::string val1("val1");
+  {
+    bufferlist bl;
+    bl.append(val1.c_str(), val1.size() + 1);
+    o.setxattr("key1", bl);
+    o.truncate(0);
+  }
+  ASSERT_EQ(0, ioctx.operate("foo", &o));
+  uint64_t size;
+  time_t mtime;
+  ASSERT_EQ(0, ioctx.stat("foo", &size, &mtime));
+  ASSERT_EQ(0U, size);
+}
+
+TEST_F(LibRadosMiscPP, BigObjectPP) {
+  bufferlist bl;
+  bl.append("abcdefg");
+  ASSERT_EQ(0, ioctx.write("foo", bl, bl.length(), 0));
+
+  {
+    ObjectWriteOperation o;
+    o.truncate(500000000000ull);
+    ASSERT_EQ(-EFBIG, ioctx.operate("foo", &o));
+  }
+  {
+    ObjectWriteOperation o;
+    o.zero(500000000000ull, 1);
+    ASSERT_EQ(-EFBIG, ioctx.operate("foo", &o));
+  }
+  {
+    ObjectWriteOperation o;
+    o.zero(1, 500000000000ull);
+    ASSERT_EQ(-EFBIG, ioctx.operate("foo", &o));
+  }
+  {
+    ObjectWriteOperation o;
+    o.zero(500000000000ull, 500000000000ull);
+    ASSERT_EQ(-EFBIG, ioctx.operate("foo", &o));
+  }
+
+#ifdef __LP64__
+  // this test only works on 64-bit platforms
+  ASSERT_EQ(-EFBIG, ioctx.write("foo", bl, bl.length(), 500000000000ull));
+#endif
+}
+
+TEST_F(LibRadosMiscPP, AioOperatePP) {
+  bool my_aio_complete = false;
+  AioCompletion *my_completion = cluster.aio_create_completion(
+         (void*)&my_aio_complete, set_completion_complete, NULL);
+  AioCompletion *my_completion_null = NULL;
+  ASSERT_NE(my_completion, my_completion_null);
+
+  ObjectWriteOperation o;
+  {
+    bufferlist bl;
+    o.write(0, bl);
+  }
+  std::string val1("val1");
+  {
+    bufferlist bl;
+    bl.append(val1.c_str(), val1.size() + 1);
+    o.setxattr("key1", bl);
+    bufferlist bl2;
+    char buf2[1024];
+    memset(buf2, 0xdd, sizeof(buf2));
+    bl2.append(buf2, sizeof(buf2));
+    o.append(bl2);
+  }
+  ASSERT_EQ(0, ioctx.aio_operate("foo", my_completion, &o));
+  ASSERT_EQ(0, my_completion->wait_for_complete_and_cb());
+  ASSERT_EQ(my_aio_complete, true);
+  my_completion->release();
+
+  uint64_t size;
+  time_t mtime;
+  ASSERT_EQ(0, ioctx.stat("foo", &size, &mtime));
+  ASSERT_EQ(1024U, size);
+}
+
+TEST_F(LibRadosMiscPP, AssertExistsPP) {
+  char buf[64];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl;
+  bl.append(buf, sizeof(buf));
+
+  ObjectWriteOperation op;
+  op.assert_exists();
+  op.write(0, bl);
+  ASSERT_EQ(-ENOENT, ioctx.operate("asdffoo", &op));
+  ASSERT_EQ(0, ioctx.create("asdffoo", true));
+  ASSERT_EQ(0, ioctx.operate("asdffoo", &op));
+  ASSERT_EQ(-EEXIST, ioctx.create("asdffoo", true));
+}
+
+TEST_F(LibRadosMiscPP, AssertVersionPP) {
+  char buf[64];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl;
+  bl.append(buf, sizeof(buf));
+
+  // Create test object...
+  ASSERT_EQ(0, ioctx.create("asdfbar", true));
+  // ...then write it again to guarantee that the
+  // (unsigned) version must be at least 1 (not 0)
+  // since we want to decrement it by 1 later.
+  ASSERT_EQ(0, ioctx.write_full("asdfbar", bl));
+
+  uint64_t v = ioctx.get_last_version();
+  ObjectWriteOperation op1;
+  op1.assert_version(v+1);
+  op1.write(0, bl);
+  ASSERT_EQ(-EOVERFLOW, ioctx.operate("asdfbar", &op1));
+  ObjectWriteOperation op2;
+  op2.assert_version(v-1);
+  op2.write(0, bl);
+  ASSERT_EQ(-ERANGE, ioctx.operate("asdfbar", &op2));
+  ObjectWriteOperation op3;
+  op3.assert_version(v);
+  op3.write(0, bl);
+  ASSERT_EQ(0, ioctx.operate("asdfbar", &op3));
+}
+
+TEST_F(LibRadosMiscPP, BigAttrPP) {
+  char buf[64];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl;
+  bl.append(buf, sizeof(buf));
+
+  ASSERT_EQ(0, ioctx.create("foo", true));
+
+  bufferlist got;
+
+  cout << "osd_max_attr_size = " << g_conf()->osd_max_attr_size << std::endl;
+  if (g_conf()->osd_max_attr_size) {
+    bl.clear();
+    got.clear();
+    bl.append(buffer::create(g_conf()->osd_max_attr_size));
+    ASSERT_EQ(0, ioctx.setxattr("foo", "one", bl));
+    ASSERT_EQ((int)bl.length(), ioctx.getxattr("foo", "one", got));
+    ASSERT_TRUE(bl.contents_equal(got));
+
+    bl.clear();
+    bl.append(buffer::create(g_conf()->osd_max_attr_size+1));
+    ASSERT_EQ(-EFBIG, ioctx.setxattr("foo", "one", bl));
+  } else {
+    cout << "osd_max_attr_size == 0; skipping test" << std::endl;
+  }
+
+  for (int i=0; i<1000; i++) {
+    bl.clear();
+    got.clear();
+    bl.append(buffer::create(std::min<uint64_t>(g_conf()->osd_max_attr_size,
+                                               1024)));
+    char n[10];
+    snprintf(n, sizeof(n), "a%d", i);
+    ASSERT_EQ(0, ioctx.setxattr("foo", n, bl));
+    ASSERT_EQ((int)bl.length(), ioctx.getxattr("foo", n, got));
+    ASSERT_TRUE(bl.contents_equal(got));
+  }
+}
+
+TEST_F(LibRadosMiscPP, CopyPP) {
+  bufferlist bl, x;
+  bl.append("hi there");
+  x.append("bar");
+
+  // small object
+  bufferlist blc = bl;
+  bufferlist xc = x;
+  ASSERT_EQ(0, ioctx.write_full("foo", blc));
+  ASSERT_EQ(0, ioctx.setxattr("foo", "myattr", xc));
+
+  version_t uv = ioctx.get_last_version();
+  {
+    // pass future version
+    ObjectWriteOperation op;
+    op.copy_from2("foo", ioctx, uv + 1, LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+    ASSERT_EQ(-EOVERFLOW, ioctx.operate("foo.copy", &op));
+  }
+  {
+    // pass old version
+    ObjectWriteOperation op;
+    op.copy_from2("foo", ioctx, uv - 1, LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+    ASSERT_EQ(-ERANGE, ioctx.operate("foo.copy", &op));
+  }
+  {
+    ObjectWriteOperation op;
+    op.copy_from2("foo", ioctx, uv, LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+    ASSERT_EQ(0, ioctx.operate("foo.copy", &op));
+
+    bufferlist bl2, x2;
+    ASSERT_EQ((int)bl.length(), ioctx.read("foo.copy", bl2, 10000, 0));
+    ASSERT_TRUE(bl.contents_equal(bl2));
+    ASSERT_EQ((int)x.length(), ioctx.getxattr("foo.copy", "myattr", x2));
+    ASSERT_TRUE(x.contents_equal(x2));
+  }
+
+  // small object without a version
+  {
+    ObjectWriteOperation op;
+    op.copy_from2("foo", ioctx, 0, LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+    ASSERT_EQ(0, ioctx.operate("foo.copy2", &op));
+
+    bufferlist bl2, x2;
+    ASSERT_EQ((int)bl.length(), ioctx.read("foo.copy2", bl2, 10000, 0));
+    ASSERT_TRUE(bl.contents_equal(bl2));
+    ASSERT_EQ((int)x.length(), ioctx.getxattr("foo.copy2", "myattr", x2));
+    ASSERT_TRUE(x.contents_equal(x2));
+  }
+
+  // do a big object
+  bl.append(buffer::create(g_conf()->osd_copyfrom_max_chunk * 3));
+  bl.zero();
+  bl.append("tail");
+  blc = bl;
+  xc = x;
+  ASSERT_EQ(0, ioctx.write_full("big", blc));
+  ASSERT_EQ(0, ioctx.setxattr("big", "myattr", xc));
+
+  {
+    ObjectWriteOperation op;
+    op.copy_from2("big", ioctx, ioctx.get_last_version(), LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
+    ASSERT_EQ(0, ioctx.operate("big.copy", &op));
+
+    bufferlist bl2, x2;
+    ASSERT_EQ((int)bl.length(), ioctx.read("big.copy", bl2, bl.length(), 0));
+    ASSERT_TRUE(bl.contents_equal(bl2));
+    ASSERT_EQ((int)x.length(), ioctx.getxattr("foo.copy", "myattr", x2));
+    ASSERT_TRUE(x.contents_equal(x2));
+  }
+
+  {
+    ObjectWriteOperation op;
+    op.copy_from2("big", ioctx, 0, LIBRADOS_OP_FLAG_FADVISE_SEQUENTIAL);
+    ASSERT_EQ(0, ioctx.operate("big.copy2", &op));
+
+    bufferlist bl2, x2;
+    ASSERT_EQ((int)bl.length(), ioctx.read("big.copy2", bl2, bl.length(), 0));
+    ASSERT_TRUE(bl.contents_equal(bl2));
+    ASSERT_EQ((int)x.length(), ioctx.getxattr("foo.copy2", "myattr", x2));
+    ASSERT_TRUE(x.contents_equal(x2));
+  }
+}
+
+class LibRadosTwoPoolsECPP : public RadosTestECPP
+{
+public:
+  LibRadosTwoPoolsECPP() {};
+  ~LibRadosTwoPoolsECPP() override {};
+protected:
+  static void SetUpTestCase() {
+    pool_name = get_temp_pool_name();
+    ASSERT_EQ("", create_one_ec_pool_pp(pool_name, s_cluster));
+    src_pool_name = get_temp_pool_name();
+    ASSERT_EQ(0, s_cluster.pool_create(src_pool_name.c_str()));
+
+    librados::IoCtx ioctx;
+    ASSERT_EQ(0, s_cluster.ioctx_create(pool_name.c_str(), ioctx));
+    ioctx.application_enable("rados", true);
+
+    librados::IoCtx src_ioctx;
+    ASSERT_EQ(0, s_cluster.ioctx_create(src_pool_name.c_str(), src_ioctx));
+    src_ioctx.application_enable("rados", true);
+  }
+  static void TearDownTestCase() {
+    ASSERT_EQ(0, s_cluster.pool_delete(src_pool_name.c_str()));
+    ASSERT_EQ(0, destroy_one_ec_pool_pp(pool_name, s_cluster));
+  }
+  static std::string src_pool_name;
+
+  void SetUp() override {
+    RadosTestECPP::SetUp();
+    ASSERT_EQ(0, cluster.ioctx_create(src_pool_name.c_str(), src_ioctx));
+    src_ioctx.set_namespace(nspace);
+  }
+  void TearDown() override {
+    // wait for maps to settle before next test
+    cluster.wait_for_latest_osdmap();
+
+    RadosTestECPP::TearDown();
+
+    cleanup_default_namespace(src_ioctx);
+    cleanup_namespace(src_ioctx, nspace);
+
+    src_ioctx.close();
+  }
+
+  librados::IoCtx src_ioctx;
+};
+std::string LibRadosTwoPoolsECPP::src_pool_name;
+
+//copy_from between ecpool and no-ecpool.
+TEST_F(LibRadosTwoPoolsECPP, CopyFrom) {
+  bufferlist z;
+  z.append_zero(4194304*2);
+  bufferlist b;
+  b.append("copyfrom");
+
+  // create big object w/ omapheader
+  {
+    ASSERT_EQ(0, src_ioctx.write_full("foo", z));
+    ASSERT_EQ(0, src_ioctx.omap_set_header("foo", b));
+    version_t uv = src_ioctx.get_last_version();
+    ObjectWriteOperation op;
+    op.copy_from("foo", src_ioctx, uv);
+    ASSERT_EQ(-EOPNOTSUPP, ioctx.operate("foo.copy", &op));
+  }
+
+  // same with small object
+  {
+    ASSERT_EQ(0, src_ioctx.omap_set_header("bar", b));
+    version_t uv = src_ioctx.get_last_version();
+    ObjectWriteOperation op;
+    op.copy_from("bar", src_ioctx, uv);
+    ASSERT_EQ(-EOPNOTSUPP, ioctx.operate("bar.copy", &op));
+  }
+}
+
+TEST_F(LibRadosMiscPP, CopyScrubPP) {
+  bufferlist inbl, bl, x;
+  for (int i=0; i<100; ++i)
+    x.append("barrrrrrrrrrrrrrrrrrrrrrrrrr");
+  bl.append(buffer::create(g_conf()->osd_copyfrom_max_chunk * 3));
+  bl.zero();
+  bl.append("tail");
+  bufferlist cbl;
+
+  map<string, bufferlist> to_set;
+  for (int i=0; i<1000; ++i)
+    to_set[string("foo") + stringify(i)] = x;
+
+  // small
+  cbl = x;
+  ASSERT_EQ(0, ioctx.write_full("small", cbl));
+  ASSERT_EQ(0, ioctx.setxattr("small", "myattr", x));
+
+  // big
+  cbl = bl;
+  ASSERT_EQ(0, ioctx.write_full("big", cbl));
+
+  // without header
+  cbl = bl;
+  ASSERT_EQ(0, ioctx.write_full("big2", cbl));
+  ASSERT_EQ(0, ioctx.setxattr("big2", "myattr", x));
+  ASSERT_EQ(0, ioctx.setxattr("big2", "myattr2", x));
+  ASSERT_EQ(0, ioctx.omap_set("big2", to_set));
+
+  // with header
+  cbl = bl;
+  ASSERT_EQ(0, ioctx.write_full("big3", cbl));
+  ASSERT_EQ(0, ioctx.omap_set_header("big3", x));
+  ASSERT_EQ(0, ioctx.omap_set("big3", to_set));
+
+  // deep scrub to ensure digests are in place
+  {
+    for (int i=0; i<10; ++i) {
+      ostringstream ss;
+      ss << "{\"prefix\": \"pg deep-scrub\", \"pgid\": \""
+        << ioctx.get_id() << "." << i
+        << "\"}";
+      cluster.mon_command(ss.str(), inbl, NULL, NULL);
+    }
+
+    // give it a few seconds to go.  this is sloppy but is usually enough time
+    cout << "waiting for initial deep scrubs..." << std::endl;
+    sleep(30);
+    cout << "done waiting, doing copies" << std::endl;
+  }
+
+  {
+    ObjectWriteOperation op;
+    op.copy_from("small", ioctx, 0);
+    ASSERT_EQ(0, ioctx.operate("small.copy", &op));
+  }
+
+  {
+    ObjectWriteOperation op;
+    op.copy_from("big", ioctx, 0);
+    ASSERT_EQ(0, ioctx.operate("big.copy", &op));
+  }
+
+  {
+    ObjectWriteOperation op;
+    op.copy_from("big2", ioctx, 0);
+    ASSERT_EQ(0, ioctx.operate("big2.copy", &op));
+  }
+
+  {
+    ObjectWriteOperation op;
+    op.copy_from("big3", ioctx, 0);
+    ASSERT_EQ(0, ioctx.operate("big3.copy", &op));
+  }
+
+  // deep scrub to ensure digests are correct
+  {
+    for (int i=0; i<10; ++i) {
+      ostringstream ss;
+      ss << "{\"prefix\": \"pg deep-scrub\", \"pgid\": \""
+        << ioctx.get_id() << "." << i
+        << "\"}";
+      cluster.mon_command(ss.str(), inbl, NULL, NULL);
+    }
+
+    // give it a few seconds to go.  this is sloppy but is usually enough time
+    cout << "waiting for final deep scrubs..." << std::endl;
+    sleep(30);
+    cout << "done waiting" << std::endl;
+  }
+}
+
+TEST_F(LibRadosMiscPP, WriteSamePP) {
+  bufferlist bl;
+  char buf[128];
+  bufferlist fl;
+  char full[128 * 4];
+  char *cmp;
+
+  /* zero the full range before using writesame */
+  memset(full, 0, sizeof(full));
+  fl.append(full, sizeof(full));
+  ASSERT_EQ(0, ioctx.write("ws", fl, fl.length(), 0));
+
+  memset(buf, 0xcc, sizeof(buf));
+  bl.clear();
+  bl.append(buf, sizeof(buf));
+  /* write the same buf four times */
+  ASSERT_EQ(0, ioctx.writesame("ws", bl, sizeof(full), 0));
+
+  /* read back the full buffer and confirm that it matches */
+  fl.clear();
+  fl.append(full, sizeof(full));
+  ASSERT_EQ((int)fl.length(), ioctx.read("ws", fl, fl.length(), 0));
+
+  for (cmp = fl.c_str(); cmp < fl.c_str() + fl.length(); cmp += sizeof(buf)) {
+    ASSERT_EQ(0, memcmp(cmp, buf, sizeof(buf)));
+  }
+
+  /* write_len not a multiple of data_len should throw error */
+  bl.clear();
+  bl.append(buf, sizeof(buf));
+  ASSERT_EQ(-EINVAL, ioctx.writesame("ws", bl, (sizeof(buf) * 4) - 1, 0));
+  ASSERT_EQ(-EINVAL,
+           ioctx.writesame("ws", bl, bl.length() / 2, 0));
+  /* write_len = data_len, i.e. same as write() */
+  ASSERT_EQ(0, ioctx.writesame("ws", bl, sizeof(buf), 0));
+  bl.clear();
+  ASSERT_EQ(-EINVAL,
+           ioctx.writesame("ws", bl, sizeof(buf), 0));
+}
+
+template <typename T>
+class LibRadosChecksum : public LibRadosMiscPP {
+public:
+  typedef typename T::alg_t alg_t;
+  typedef typename T::value_t value_t;
+  typedef typename alg_t::init_value_t init_value_t;
+
+  static const rados_checksum_type_t type = T::type;
+
+  bufferlist content_bl;
+
+  using LibRadosMiscPP::SetUpTestCase;
+  using LibRadosMiscPP::TearDownTestCase;
+
+  void SetUp() override {
+    LibRadosMiscPP::SetUp();
+
+    std::string content(4096, '\0');
+    for (size_t i = 0; i < content.length(); ++i) {
+      content[i] = static_cast<char>(rand() % (126 - 33) + 33);
+    }
+    content_bl.append(content);
+    ASSERT_EQ(0, ioctx.write("foo", content_bl, content_bl.length(), 0));
+  }
+};
+
+template <rados_checksum_type_t _type, typename AlgT, typename ValueT>
+class LibRadosChecksumParams {
+public:
+  typedef AlgT alg_t;
+  typedef ValueT value_t;
+  static const rados_checksum_type_t type = _type;
+};
+
+typedef ::testing::Types<
+    LibRadosChecksumParams<LIBRADOS_CHECKSUM_TYPE_XXHASH32,
+                          Checksummer::xxhash32, uint32_t>,
+    LibRadosChecksumParams<LIBRADOS_CHECKSUM_TYPE_XXHASH64,
+                          Checksummer::xxhash64, uint64_t>,
+    LibRadosChecksumParams<LIBRADOS_CHECKSUM_TYPE_CRC32C,
+                          Checksummer::crc32c, uint32_t>
+  > LibRadosChecksumTypes;
+
+TYPED_TEST_CASE(LibRadosChecksum, LibRadosChecksumTypes);
+
+TYPED_TEST(LibRadosChecksum, Subset) {
+  uint32_t chunk_size = 1024;
+  uint32_t csum_count = this->content_bl.length() / chunk_size;
+
+  typename TestFixture::init_value_t init_value = -1;
+  bufferlist init_value_bl;
+  encode(init_value, init_value_bl);
+
+  std::vector<bufferlist> checksum_bls(csum_count);
+  std::vector<int> checksum_rvals(csum_count);
+
+  // individual checksum ops for each chunk
+  ObjectReadOperation op;
+  for (uint32_t i = 0; i < csum_count; ++i) {
+    op.checksum(TestFixture::type, init_value_bl, i * chunk_size, chunk_size,
+               0, &checksum_bls[i], &checksum_rvals[i]);
+  }
+  ASSERT_EQ(0, this->ioctx.operate("foo", &op, NULL));
+
+  for (uint32_t i = 0; i < csum_count; ++i) {
+    ASSERT_EQ(0, checksum_rvals[i]);
+
+    auto bl_it = checksum_bls[i].cbegin();
+    uint32_t count;
+    decode(count, bl_it);
+    ASSERT_EQ(1U, count);
+
+    typename TestFixture::value_t value;
+    decode(value, bl_it);
+
+    bufferlist content_sub_bl;
+    content_sub_bl.substr_of(this->content_bl, i * chunk_size, chunk_size);
+
+    typename TestFixture::value_t expected_value;
+    bufferptr expected_value_bp = buffer::create_static(
+      sizeof(expected_value), reinterpret_cast<char*>(&expected_value));
+    Checksummer::template calculate<typename TestFixture::alg_t>(
+      init_value, chunk_size, 0, chunk_size, content_sub_bl,
+      &expected_value_bp);
+    ASSERT_EQ(expected_value, value);
+  }
+}
+
+TYPED_TEST(LibRadosChecksum, Chunked) {
+  uint32_t chunk_size = 1024;
+  uint32_t csum_count = this->content_bl.length() / chunk_size;
+
+  typename TestFixture::init_value_t init_value = -1;
+  bufferlist init_value_bl;
+  encode(init_value, init_value_bl);
+
+  bufferlist checksum_bl;
+  int checksum_rval;
+
+  // single op with chunked checksum results
+  ObjectReadOperation op;
+  op.checksum(TestFixture::type, init_value_bl, 0, this->content_bl.length(),
+             chunk_size, &checksum_bl, &checksum_rval);
+  ASSERT_EQ(0, this->ioctx.operate("foo", &op, NULL));
+  ASSERT_EQ(0, checksum_rval);
+
+  auto bl_it = checksum_bl.cbegin();
+  uint32_t count;
+  decode(count, bl_it);
+  ASSERT_EQ(csum_count, count);
+
+  std::vector<typename TestFixture::value_t> expected_values(csum_count);
+  bufferptr expected_values_bp = buffer::create_static(
+    csum_count * sizeof(typename TestFixture::value_t),
+    reinterpret_cast<char*>(&expected_values[0]));
+
+  Checksummer::template calculate<typename TestFixture::alg_t>(
+    init_value, chunk_size, 0, this->content_bl.length(), this->content_bl,
+    &expected_values_bp);
+
+  for (uint32_t i = 0; i < csum_count; ++i) {
+    typename TestFixture::value_t value;
+    decode(value, bl_it);
+    ASSERT_EQ(expected_values[i], value);
+  }
+}
+
+TEST_F(LibRadosMiscPP, CmpExtPP) {
+  bufferlist cmp_bl, bad_cmp_bl, write_bl;
+  char stored_str[] = "1234567891";
+  char mismatch_str[] = "1234577777";
+
+  write_bl.append(stored_str);
+  ioctx.write("cmpextpp", write_bl, write_bl.length(), 0);
+  cmp_bl.append(stored_str);
+  ASSERT_EQ(0, ioctx.cmpext("cmpextpp", 0, cmp_bl));
+
+  bad_cmp_bl.append(mismatch_str);
+  ASSERT_EQ(-MAX_ERRNO - 5, ioctx.cmpext("cmpextpp", 0, bad_cmp_bl));
+}
+
+TEST_F(LibRadosMiscPP, Applications) {
+  bufferlist inbl, outbl;
+  string outs;
+  ASSERT_EQ(0, cluster.mon_command("{\"prefix\": \"osd dump\"}",
+                                  inbl, &outbl, &outs));
+  ASSERT_LT(0u, outbl.length());
+  ASSERT_LE(0u, outs.length());
+  if (!std::regex_search(outbl.to_str(),
+                        std::regex("require_osd_release [l-z]"))) {
+    std::cout << "SKIPPING";
+    return;
+  }
+
+  std::set<std::string> expected_apps = {"rados"};
+  std::set<std::string> apps;
+  ASSERT_EQ(0, ioctx.application_list(&apps));
+  ASSERT_EQ(expected_apps, apps);
+
+  ASSERT_EQ(0, ioctx.application_enable("app1", true));
+  ASSERT_EQ(-EPERM, ioctx.application_enable("app2", false));
+  ASSERT_EQ(0, ioctx.application_enable("app2", true));
+
+  expected_apps = {"app1", "app2", "rados"};
+  ASSERT_EQ(0, ioctx.application_list(&apps));
+  ASSERT_EQ(expected_apps, apps);
+
+  std::map<std::string, std::string> expected_meta;
+  std::map<std::string, std::string> meta;
+  ASSERT_EQ(-ENOENT, ioctx.application_metadata_list("dne", &meta));
+  ASSERT_EQ(0, ioctx.application_metadata_list("app1", &meta));
+  ASSERT_EQ(expected_meta, meta);
+
+  ASSERT_EQ(-ENOENT, ioctx.application_metadata_set("dne", "key1", "value1"));
+  ASSERT_EQ(0, ioctx.application_metadata_set("app1", "key1", "value1"));
+  ASSERT_EQ(0, ioctx.application_metadata_set("app1", "key2", "value2"));
+
+  expected_meta = {{"key1", "value1"}, {"key2", "value2"}};
+  ASSERT_EQ(0, ioctx.application_metadata_list("app1", &meta));
+  ASSERT_EQ(expected_meta, meta);
+
+  ASSERT_EQ(0, ioctx.application_metadata_remove("app1", "key1"));
+
+  expected_meta = {{"key2", "value2"}};
+  ASSERT_EQ(0, ioctx.application_metadata_list("app1", &meta));
+  ASSERT_EQ(expected_meta, meta);
+}
+
+TEST_F(LibRadosMiscECPP, CompareExtentRange) {
+  bufferlist bl1;
+  bl1.append("ceph");
+  ObjectWriteOperation write;
+  write.write(0, bl1);
+  ASSERT_EQ(0, ioctx.operate("foo", &write));
+
+  bufferlist bl2;
+  bl2.append("ph");
+  bl2.append(std::string(2, '\0'));
+  ObjectReadOperation read1;
+  read1.cmpext(2, bl2, nullptr);
+  ASSERT_EQ(0, ioctx.operate("foo", &read1, nullptr));
+
+  bufferlist bl3;
+  bl3.append(std::string(4, '\0'));
+  ObjectReadOperation read2;
+  read2.cmpext(2097152, bl3, nullptr);
+  ASSERT_EQ(0, ioctx.operate("foo", &read2, nullptr));
+}
+
+TEST_F(LibRadosMiscPP, MinCompatOSD) {
+  int8_t require_osd_release;
+  ASSERT_EQ(0, cluster.get_min_compatible_osd(&require_osd_release));
+  ASSERT_LE(-1, require_osd_release);
+  ASSERT_GT(CEPH_RELEASE_MAX, require_osd_release);
+}
+
+TEST_F(LibRadosMiscPP, MinCompatClient) {
+  int8_t min_compat_client;
+  int8_t require_min_compat_client;
+  ASSERT_EQ(0, cluster.get_min_compatible_client(&min_compat_client,
+                                                 &require_min_compat_client));
+  ASSERT_LE(-1, min_compat_client);
+  ASSERT_GT(CEPH_RELEASE_MAX, min_compat_client);
+
+  ASSERT_LE(-1, require_min_compat_client);
+  ASSERT_GT(CEPH_RELEASE_MAX, require_min_compat_client);
+}
+
+TEST_F(LibRadosMiscPP, Conf) {
+  const char* const option = "bluestore_throttle_bytes";
+  size_t new_size = 1 << 20;
+  std::string original;
+  ASSERT_EQ(0, cluster.conf_get(option, original));
+  auto restore_setting = make_scope_guard([&] {
+    cluster.conf_set(option, original.c_str());
+  });
+  std::string expected = std::to_string(new_size);
+  ASSERT_EQ(0, cluster.conf_set(option, expected.c_str()));
+  std::string actual;
+  ASSERT_EQ(0, cluster.conf_get(option, actual));
+  ASSERT_EQ(expected, actual);
+}
index 74c89595bf12b54f2c12008f28a3f5e422d3d586..14906651922f08570213b0e015c1e46ce85c60b0 100644 (file)
@@ -1,9 +1,8 @@
-#include "include/rados/librados.h"
-#include "test/librados/test.h"
-
-#include "gtest/gtest.h"
 #include <errno.h>
 #include <vector>
+#include "gtest/gtest.h"
+#include "include/rados/librados.h"
+#include "test/librados/test.h"
 
 #define POOL_LIST_BUF_SZ 32768
 
index da37fb3d62f3519bbe79d356553a5e2f8ecf41a9..9bc9cd5069128cabc86dfa3cd46637f189328323 100644 (file)
@@ -69,94 +69,3 @@ TEST(LibRadosService, Status) {
   }
   rados_shutdown(cluster);
 }
-
-TEST(LibRadosServicePP, RegisterEarly) {
-  Rados cluster;
-  cluster.init("admin");
-  ASSERT_EQ(0, cluster.conf_read_file(NULL));
-  cluster.conf_parse_env(NULL);
-  string name = string("pid") + stringify(getpid());
-  ASSERT_EQ(0, cluster.service_daemon_register(
-             "laundry", name, {{"foo", "bar"}, {"this", "that"}}));
-  ASSERT_EQ(-EEXIST, cluster.service_daemon_register(
-             "laundry", name, {{"foo", "bar"}, {"this", "that"}}));
-  ASSERT_EQ(0, cluster.connect());
-  sleep(5);
-  cluster.shutdown();
-}
-
-TEST(LibRadosServicePP, RegisterLate) {
-  Rados cluster;
-  cluster.init("admin");
-  ASSERT_EQ(0, cluster.conf_read_file(NULL));
-  cluster.conf_parse_env(NULL);
-  ASSERT_EQ("", connect_cluster_pp(cluster));
-  string name = string("pid") + stringify(getpid());
-  ASSERT_EQ(0, cluster.service_daemon_register(
-             "laundry", name, {{"foo", "bar"}, {"this", "that"}}));
-}
-
-TEST(LibRadosServicePP, Status) {
-  Rados cluster;
-  cluster.init("admin");
-  ASSERT_EQ(0, cluster.conf_read_file(NULL));
-  cluster.conf_parse_env(NULL);
-  string name = string("pid") + stringify(getpid());
-  ASSERT_EQ(-ENOTCONN, cluster.service_daemon_update_status(
-             {{"testing", "starting"}}));
-  ASSERT_EQ(0, cluster.connect());
-  ASSERT_EQ(0, cluster.service_daemon_register(
-             "laundry", name, {{"foo", "bar"}, {"this", "that"}}));
-  for (int i=0; i<20; ++i) {
-    ASSERT_EQ(0, cluster.service_daemon_update_status({
-         {"testing", "running"},
-         {"count", stringify(i)}
-       }));
-    sleep(1);
-  }
-  cluster.shutdown();
-}
-
-TEST(LibRadosServicePP, Close) {
-  int tries = 20;
-  string name = string("close-test-pid") + stringify(getpid());
-  int i;
-  for (i = 0; i < tries; ++i) {
-    cout << "attempt " << i << " of " << tries << std::endl;
-    {
-      Rados cluster;
-      cluster.init("admin");
-      ASSERT_EQ(0, cluster.conf_read_file(NULL));
-      cluster.conf_parse_env(NULL);
-      ASSERT_EQ(0, cluster.connect());
-      ASSERT_EQ(0, cluster.service_daemon_register(
-                 "laundry", name, {{"foo", "bar"}, {"this", "that"}}));
-      sleep(3); // let it register
-      cluster.shutdown();
-    }
-    // mgr updates servicemap every tick
-    //sleep(g_conf().get_val<int64_t>("mgr_tick_period"));
-    std::this_thread::sleep_for(g_conf().get_val<std::chrono::seconds>(
-                                 "mgr_tick_period"));
-    // make sure we are deregistered
-    {
-      Rados cluster;
-      cluster.init("admin");
-      ASSERT_EQ(0, cluster.conf_read_file(NULL));
-      cluster.conf_parse_env(NULL);
-      ASSERT_EQ(0, cluster.connect());
-      bufferlist inbl, outbl;
-      ASSERT_EQ(0, cluster.mon_command("{\"prefix\": \"service dump\"}",
-                                      inbl, &outbl, NULL));
-      string s = outbl.to_str();
-      cluster.shutdown();
-
-      if (s.find(name) != string::npos) {
-       cout << " failed to deregister:\n" << s << std::endl;
-      } else {
-       break;
-      }
-    }
-  }
-  ASSERT_LT(i, tries);
-}
diff --git a/src/test/librados/service_cxx.cc b/src/test/librados/service_cxx.cc
new file mode 100644 (file)
index 0000000..40869f0
--- /dev/null
@@ -0,0 +1,104 @@
+#include <algorithm>
+#include <thread>
+#include <errno.h>
+#include "gtest/gtest.h"
+
+#include "include/rados/librados.hpp"
+#include "include/stringify.h"
+#include "common/config_proxy.h"
+#include "test/librados/test_cxx.h"
+#include "test/librados/testcase_cxx.h"
+#include "test/unit.cc"
+
+using namespace librados;
+
+TEST(LibRadosServicePP, RegisterEarly) {
+  Rados cluster;
+  cluster.init("admin");
+  ASSERT_EQ(0, cluster.conf_read_file(NULL));
+  cluster.conf_parse_env(NULL);
+  string name = string("pid") + stringify(getpid());
+  ASSERT_EQ(0, cluster.service_daemon_register(
+             "laundry", name, {{"foo", "bar"}, {"this", "that"}}));
+  ASSERT_EQ(-EEXIST, cluster.service_daemon_register(
+             "laundry", name, {{"foo", "bar"}, {"this", "that"}}));
+  ASSERT_EQ(0, cluster.connect());
+  sleep(5);
+  cluster.shutdown();
+}
+
+TEST(LibRadosServicePP, RegisterLate) {
+  Rados cluster;
+  cluster.init("admin");
+  ASSERT_EQ(0, cluster.conf_read_file(NULL));
+  cluster.conf_parse_env(NULL);
+  ASSERT_EQ("", connect_cluster_pp(cluster));
+  string name = string("pid") + stringify(getpid());
+  ASSERT_EQ(0, cluster.service_daemon_register(
+             "laundry", name, {{"foo", "bar"}, {"this", "that"}}));
+}
+
+TEST(LibRadosServicePP, Status) {
+  Rados cluster;
+  cluster.init("admin");
+  ASSERT_EQ(0, cluster.conf_read_file(NULL));
+  cluster.conf_parse_env(NULL);
+  string name = string("pid") + stringify(getpid());
+  ASSERT_EQ(-ENOTCONN, cluster.service_daemon_update_status(
+             {{"testing", "starting"}}));
+  ASSERT_EQ(0, cluster.connect());
+  ASSERT_EQ(0, cluster.service_daemon_register(
+             "laundry", name, {{"foo", "bar"}, {"this", "that"}}));
+  for (int i=0; i<20; ++i) {
+    ASSERT_EQ(0, cluster.service_daemon_update_status({
+         {"testing", "running"},
+         {"count", stringify(i)}
+       }));
+    sleep(1);
+  }
+  cluster.shutdown();
+}
+
+TEST(LibRadosServicePP, Close) {
+  int tries = 20;
+  string name = string("close-test-pid") + stringify(getpid());
+  int i;
+  for (i = 0; i < tries; ++i) {
+    cout << "attempt " << i << " of " << tries << std::endl;
+    {
+      Rados cluster;
+      cluster.init("admin");
+      ASSERT_EQ(0, cluster.conf_read_file(NULL));
+      cluster.conf_parse_env(NULL);
+      ASSERT_EQ(0, cluster.connect());
+      ASSERT_EQ(0, cluster.service_daemon_register(
+                 "laundry", name, {{"foo", "bar"}, {"this", "that"}}));
+      sleep(3); // let it register
+      cluster.shutdown();
+    }
+    // mgr updates servicemap every tick
+    //sleep(g_conf().get_val<int64_t>("mgr_tick_period"));
+    std::this_thread::sleep_for(g_conf().get_val<std::chrono::seconds>(
+                                 "mgr_tick_period"));
+    // make sure we are deregistered
+    {
+      Rados cluster;
+      cluster.init("admin");
+      ASSERT_EQ(0, cluster.conf_read_file(NULL));
+      cluster.conf_parse_env(NULL);
+      ASSERT_EQ(0, cluster.connect());
+      bufferlist inbl, outbl;
+      ASSERT_EQ(0, cluster.mon_command("{\"prefix\": \"service dump\"}",
+                                      inbl, &outbl, NULL));
+      string s = outbl.to_str();
+      cluster.shutdown();
+
+      if (s.find(name) != string::npos) {
+       cout << " failed to deregister:\n" << s << std::endl;
+      } else {
+       break;
+      }
+    }
+  }
+  ASSERT_LT(i, tries);
+}
index 49bd9961efcb0edb75a81d1185773a02ae1759aa..966ec19add6ce9e0920a60ea22f93e8406ffdf16 100644 (file)
@@ -1,5 +1,4 @@
 #include "include/rados.h"
-#include "include/rados/librados.hpp"
 #include "test/librados/test.h"
 #include "test/librados/TestCase.h"
 
@@ -8,17 +7,12 @@
 #include "gtest/gtest.h"
 #include <string>
 
-using namespace librados;
 using std::string;
 
 typedef RadosTest LibRadosSnapshots;
 typedef RadosTest LibRadosSnapshotsSelfManaged;
-typedef RadosTestPP LibRadosSnapshotsPP;
-typedef RadosTestPP LibRadosSnapshotsSelfManagedPP;
 typedef RadosTestEC LibRadosSnapshotsEC;
 typedef RadosTestEC LibRadosSnapshotsSelfManagedEC;
-typedef RadosTestECPP LibRadosSnapshotsECPP;
-typedef RadosTestECPP LibRadosSnapshotsSelfManagedECPP;
 
 const int bufsize = 128;
 
@@ -36,25 +30,6 @@ TEST_F(LibRadosSnapshots, SnapList) {
   EXPECT_EQ(0, rados_ioctx_snap_remove(ioctx, "snap1"));
 }
 
-TEST_F(LibRadosSnapshotsPP, SnapListPP) {
-  char buf[bufsize];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
-  ASSERT_FALSE(cluster.get_pool_is_selfmanaged_snaps_mode(pool_name));
-  ASSERT_EQ(0, ioctx.snap_create("snap1"));
-  ASSERT_FALSE(cluster.get_pool_is_selfmanaged_snaps_mode(pool_name));
-  std::vector<snap_t> snaps;
-  EXPECT_EQ(0, ioctx.snap_list(&snaps));
-  EXPECT_EQ(1U, snaps.size());
-  snap_t rid;
-  EXPECT_EQ(0, ioctx.snap_lookup("snap1", &rid));
-  EXPECT_EQ(rid, snaps[0]);
-  EXPECT_EQ(0, ioctx.snap_remove("snap1"));
-  ASSERT_FALSE(cluster.get_pool_is_selfmanaged_snaps_mode(pool_name));
-}
-
 TEST_F(LibRadosSnapshots, SnapRemove) {
   char buf[bufsize];
   memset(buf, 0xcc, sizeof(buf));
@@ -67,19 +42,6 @@ TEST_F(LibRadosSnapshots, SnapRemove) {
   ASSERT_EQ(-ENOENT, rados_ioctx_snap_lookup(ioctx, "snap1", &rid));
 }
 
-TEST_F(LibRadosSnapshotsPP, SnapRemovePP) {
-  char buf[bufsize];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
-  ASSERT_EQ(0, ioctx.snap_create("snap1"));
-  rados_snap_t rid;
-  ASSERT_EQ(0, ioctx.snap_lookup("snap1", &rid));
-  ASSERT_EQ(0, ioctx.snap_remove("snap1"));
-  ASSERT_EQ(-ENOENT, ioctx.snap_lookup("snap1", &rid));
-}
-
 TEST_F(LibRadosSnapshots, Rollback) {
   char buf[bufsize];
   memset(buf, 0xcc, sizeof(buf));
@@ -95,25 +57,6 @@ TEST_F(LibRadosSnapshots, Rollback) {
   EXPECT_EQ(0, rados_ioctx_snap_remove(ioctx, "snap1"));
 }
 
-TEST_F(LibRadosSnapshotsPP, RollbackPP) {
-  char buf[bufsize];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
-  ASSERT_EQ(0, ioctx.snap_create("snap1"));
-  char buf2[sizeof(buf)];
-  memset(buf2, 0xdd, sizeof(buf2));
-  bufferlist bl2;
-  bl2.append(buf2, sizeof(buf2));
-  EXPECT_EQ(0, ioctx.write_full("foo", bl2));
-  EXPECT_EQ(0, ioctx.snap_rollback("foo", "snap1"));
-  bufferlist bl3;
-  EXPECT_EQ((int)sizeof(buf), ioctx.read("foo", bl3, sizeof(buf), 0));
-  EXPECT_EQ(0, memcmp(buf, bl3.c_str(), sizeof(buf)));
-  EXPECT_EQ(0, ioctx.snap_remove("snap1"));
-}
-
 TEST_F(LibRadosSnapshots, SnapGetName) {
   char buf[bufsize];
   memset(buf, 0xcc, sizeof(buf));
@@ -131,42 +74,6 @@ TEST_F(LibRadosSnapshots, SnapGetName) {
   EXPECT_EQ(0, rados_ioctx_snap_remove(ioctx, "snapfoo"));
 }
 
-TEST_F(LibRadosSnapshotsPP, SnapGetNamePP) {
-  char buf[bufsize];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl;
-  bl.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
-  ASSERT_EQ(0, ioctx.snap_create("snapfoo"));
-  rados_snap_t rid;
-  EXPECT_EQ(0, ioctx.snap_lookup("snapfoo", &rid));
-  EXPECT_EQ(-ENOENT, ioctx.snap_lookup("snapbar", &rid));
-  std::string name;
-  EXPECT_EQ(0, ioctx.snap_get_name(rid, &name));
-  time_t snaptime;
-  EXPECT_EQ(0, ioctx.snap_get_stamp(rid, &snaptime));
-  EXPECT_EQ(0, strcmp(name.c_str(), "snapfoo"));
-  EXPECT_EQ(0, ioctx.snap_remove("snapfoo"));
-}
-
-TEST_F(LibRadosSnapshotsPP, SnapCreateRemovePP) {
-  // reproduces http://tracker.ceph.com/issues/10262
-  bufferlist bl;
-  bl.append("foo");
-  ASSERT_EQ(0, ioctx.write("foo", bl, bl.length(), 0));
-  ASSERT_EQ(0, ioctx.snap_create("snapfoo"));
-  ASSERT_EQ(0, ioctx.remove("foo"));
-  ASSERT_EQ(0, ioctx.snap_create("snapbar"));
-
-  std::unique_ptr<librados::ObjectWriteOperation> op(new librados::ObjectWriteOperation());
-  op->create(false);
-  op->remove();
-  ASSERT_EQ(0, ioctx.operate("foo", op.get()));
-
-  EXPECT_EQ(0, ioctx.snap_remove("snapfoo"));
-  EXPECT_EQ(0, ioctx.snap_remove("snapbar"));
-}
-
 TEST_F(LibRadosSnapshotsSelfManaged, Snap) {
   std::vector<uint64_t> my_snaps;
   my_snaps.push_back(-2);
@@ -246,355 +153,6 @@ TEST_F(LibRadosSnapshotsSelfManaged, Rollback) {
   ASSERT_EQ(0, rados_remove(ioctx, "foo"));
 }
 
-TEST_F(LibRadosSnapshotsSelfManagedPP, SnapPP) {
-  std::vector<uint64_t> my_snaps;
-  my_snaps.push_back(-2);
-  ASSERT_FALSE(cluster.get_pool_is_selfmanaged_snaps_mode(pool_name));
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps.back()));
-  ASSERT_TRUE(cluster.get_pool_is_selfmanaged_snaps_mode(pool_name));
-  ::std::reverse(my_snaps.begin(), my_snaps.end()); 
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
-  ::std::reverse(my_snaps.begin(), my_snaps.end());
-  char buf[bufsize];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
-
-  my_snaps.push_back(-2);
-  librados::AioCompletion *completion = cluster.aio_create_completion();
-  ioctx.aio_selfmanaged_snap_create(&my_snaps.back(), completion);
-  ASSERT_EQ(0, completion->wait_for_complete());
-  completion->release();
-  ::std::reverse(my_snaps.begin(), my_snaps.end());
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
-  ::std::reverse(my_snaps.begin(), my_snaps.end());
-  char buf2[sizeof(buf)];
-  memset(buf2, 0xdd, sizeof(buf2));
-  bufferlist bl2;
-  bl2.append(buf2, sizeof(buf2));
-  ASSERT_EQ(0, ioctx.write("foo", bl2, sizeof(buf2), 0));
-
-  ioctx.snap_set_read(my_snaps[1]);
-  bufferlist bl3;
-  ASSERT_EQ((int)sizeof(buf), ioctx.read("foo", bl3, sizeof(buf), 0));
-  ASSERT_EQ(0, memcmp(bl3.c_str(), buf, sizeof(buf)));
-
-  completion = cluster.aio_create_completion();
-  ioctx.aio_selfmanaged_snap_remove(my_snaps.back(), completion);
-  ASSERT_EQ(0, completion->wait_for_complete());
-  completion->release();
-  my_snaps.pop_back();
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_remove(my_snaps.back()));
-  my_snaps.pop_back();
-  ioctx.snap_set_read(LIBRADOS_SNAP_HEAD);
-  ASSERT_TRUE(cluster.get_pool_is_selfmanaged_snaps_mode(pool_name));
-  ASSERT_EQ(0, ioctx.remove("foo"));
-}
-
-TEST_F(LibRadosSnapshotsSelfManagedPP, RollbackPP) {
-  std::vector<uint64_t> my_snaps;
-  IoCtx readioctx;
-  ASSERT_EQ(0, cluster.ioctx_create(pool_name.c_str(), readioctx));
-  readioctx.set_namespace(nspace);
-  readioctx.snap_set_read(LIBRADOS_SNAP_DIR);
-
-  my_snaps.push_back(-2);
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps.back()));
-  ::std::reverse(my_snaps.begin(), my_snaps.end());
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
-  ::std::reverse(my_snaps.begin(), my_snaps.end());
-  char buf[bufsize];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  //Write 3 consecutive buffers
-  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
-  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), bufsize));
-  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), bufsize*2));
-
-  snap_set_t ss;
-
-  snap_t head = SNAP_HEAD;
-  ASSERT_EQ(0, readioctx.list_snaps("foo", &ss));
-  ASSERT_EQ(1u, ss.clones.size());
-  ASSERT_EQ(head, ss.clones[0].cloneid);
-  ASSERT_EQ(0u, ss.clones[0].snaps.size());
-  ASSERT_EQ(0u, ss.clones[0].overlap.size());
-  ASSERT_EQ(384u, ss.clones[0].size);
-
-  my_snaps.push_back(-2);
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps.back()));
-  ::std::reverse(my_snaps.begin(), my_snaps.end());
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
-  ::std::reverse(my_snaps.begin(), my_snaps.end());
-  char buf2[sizeof(buf)];
-  memset(buf2, 0xdd, sizeof(buf2));
-  bufferlist bl2;
-  bl2.append(buf2, sizeof(buf2));
-  //Change the middle buffer
-  ASSERT_EQ(0, ioctx.write("foo", bl2, sizeof(buf2), bufsize));
-  //Add another after
-  ASSERT_EQ(0, ioctx.write("foo", bl2, sizeof(buf2), bufsize*3));
-
-  ASSERT_EQ(-EINVAL, ioctx.list_snaps("foo", &ss));
-  ObjectReadOperation o;
-  o.list_snaps(&ss, NULL);
-  ASSERT_EQ(-EINVAL, ioctx.operate("foo", &o, NULL));
-
-  ASSERT_EQ(0, readioctx.list_snaps("foo", &ss));
-  ASSERT_EQ(2u, ss.clones.size());
-  ASSERT_EQ(my_snaps[1], ss.clones[0].cloneid);
-  ASSERT_EQ(1u, ss.clones[0].snaps.size());
-  ASSERT_EQ(my_snaps[1], ss.clones[0].snaps[0]);
-  ASSERT_EQ(2u, ss.clones[0].overlap.size());
-  ASSERT_EQ(0u, ss.clones[0].overlap[0].first);
-  ASSERT_EQ(128u, ss.clones[0].overlap[0].second);
-  ASSERT_EQ(256u, ss.clones[0].overlap[1].first);
-  ASSERT_EQ(128u, ss.clones[0].overlap[1].second);
-  ASSERT_EQ(384u, ss.clones[0].size);
-  ASSERT_EQ(head, ss.clones[1].cloneid);
-  ASSERT_EQ(0u, ss.clones[1].snaps.size());
-  ASSERT_EQ(0u, ss.clones[1].overlap.size());
-  ASSERT_EQ(512u, ss.clones[1].size);
-
-  ioctx.selfmanaged_snap_rollback("foo", my_snaps[1]);
-
-  bufferlist bl3;
-  ASSERT_EQ((int)sizeof(buf), ioctx.read("foo", bl3, sizeof(buf), 0));
-  ASSERT_EQ(0, memcmp(bl3.c_str(), buf, sizeof(buf)));
-  ASSERT_EQ((int)sizeof(buf), ioctx.read("foo", bl3, sizeof(buf), bufsize));
-  ASSERT_EQ(0, memcmp(bl3.c_str(), buf, sizeof(buf)));
-  ASSERT_EQ((int)sizeof(buf), ioctx.read("foo", bl3, sizeof(buf), bufsize*2));
-  ASSERT_EQ(0, memcmp(bl3.c_str(), buf, sizeof(buf)));
-  ASSERT_EQ((int)0, ioctx.read("foo", bl3, sizeof(buf), bufsize*3));
-
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_remove(my_snaps.back()));
-  my_snaps.pop_back();
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_remove(my_snaps.back()));
-  my_snaps.pop_back();
-  readioctx.close();
-}
-
-TEST_F(LibRadosSnapshotsSelfManagedPP, SnapOverlapPP) {
-  std::vector<uint64_t> my_snaps;
-  IoCtx readioctx;
-  ASSERT_EQ(0, cluster.ioctx_create(pool_name.c_str(), readioctx));
-  readioctx.set_namespace(nspace);
-  readioctx.snap_set_read(LIBRADOS_SNAP_DIR);
-
-  my_snaps.push_back(-2);
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps.back()));
-  ::std::reverse(my_snaps.begin(), my_snaps.end());
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
-  ::std::reverse(my_snaps.begin(), my_snaps.end());
-  char buf[bufsize];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
-  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), bufsize*2));
-  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), bufsize*4));
-  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), bufsize*6));
-  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), bufsize*8));
-
-  snap_set_t ss;
-  snap_t head = SNAP_HEAD;
-  ASSERT_EQ(0, readioctx.list_snaps("foo", &ss));
-  ASSERT_EQ(1u, ss.clones.size());
-  ASSERT_EQ(head, ss.clones[0].cloneid);
-  ASSERT_EQ(0u, ss.clones[0].snaps.size());
-  ASSERT_EQ(0u, ss.clones[0].overlap.size());
-  ASSERT_EQ(1152u, ss.clones[0].size);
-
-  my_snaps.push_back(-2);
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps.back()));
-  ::std::reverse(my_snaps.begin(), my_snaps.end());
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
-  ::std::reverse(my_snaps.begin(), my_snaps.end());
-  char buf2[sizeof(buf)];
-  memset(buf2, 0xdd, sizeof(buf2));
-  bufferlist bl2;
-  bl2.append(buf2, sizeof(buf2));
-  ASSERT_EQ(0, ioctx.write("foo", bl2, sizeof(buf2), bufsize*1));
-  ASSERT_EQ(0, ioctx.write("foo", bl2, sizeof(buf2), bufsize*3));
-  ASSERT_EQ(0, ioctx.write("foo", bl2, sizeof(buf2), bufsize*5));
-  ASSERT_EQ(0, ioctx.write("foo", bl2, sizeof(buf2), bufsize*7));
-  ASSERT_EQ(0, ioctx.write("foo", bl2, sizeof(buf2), bufsize*9));
-
-  ASSERT_EQ(0, readioctx.list_snaps("foo", &ss));
-  ASSERT_EQ(2u, ss.clones.size());
-  ASSERT_EQ(my_snaps[1], ss.clones[0].cloneid);
-  ASSERT_EQ(1u, ss.clones[0].snaps.size());
-  ASSERT_EQ(my_snaps[1], ss.clones[0].snaps[0]);
-  ASSERT_EQ(5u, ss.clones[0].overlap.size());
-  ASSERT_EQ(0u, ss.clones[0].overlap[0].first);
-  ASSERT_EQ(128u, ss.clones[0].overlap[0].second);
-  ASSERT_EQ(256u, ss.clones[0].overlap[1].first);
-  ASSERT_EQ(128u, ss.clones[0].overlap[1].second);
-  ASSERT_EQ(512u, ss.clones[0].overlap[2].first);
-  ASSERT_EQ(128u, ss.clones[0].overlap[2].second);
-  ASSERT_EQ(768u, ss.clones[0].overlap[3].first);
-  ASSERT_EQ(128u, ss.clones[0].overlap[3].second);
-  ASSERT_EQ(1024u, ss.clones[0].overlap[4].first);
-  ASSERT_EQ(128u, ss.clones[0].overlap[4].second);
-  ASSERT_EQ(1152u, ss.clones[0].size);
-  ASSERT_EQ(head, ss.clones[1].cloneid);
-  ASSERT_EQ(0u, ss.clones[1].snaps.size());
-  ASSERT_EQ(0u, ss.clones[1].overlap.size());
-  ASSERT_EQ(1280u, ss.clones[1].size);
-
-  my_snaps.push_back(-2);
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps.back()));
-  ::std::reverse(my_snaps.begin(), my_snaps.end());
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
-  ::std::reverse(my_snaps.begin(), my_snaps.end());
-
-  char buf3[sizeof(buf)];
-  memset(buf3, 0xee, sizeof(buf3));
-  bufferlist bl4;
-  bl4.append(buf3, sizeof(buf3));
-  ASSERT_EQ(0, ioctx.write("foo", bl2, sizeof(buf3), bufsize*1));
-  ASSERT_EQ(0, ioctx.write("foo", bl2, sizeof(buf3), bufsize*4));
-  ASSERT_EQ(0, ioctx.write("foo", bl2, sizeof(buf3), bufsize*5));
-  ASSERT_EQ(0, ioctx.write("foo", bl2, sizeof(buf3), bufsize*8));
-
-  ASSERT_EQ(0, readioctx.list_snaps("foo", &ss));
-  ASSERT_EQ(3u, ss.clones.size());
-  ASSERT_EQ(my_snaps[1], ss.clones[0].cloneid);
-  ASSERT_EQ(1u, ss.clones[0].snaps.size());
-  ASSERT_EQ(my_snaps[1], ss.clones[0].snaps[0]);
-  ASSERT_EQ(5u, ss.clones[0].overlap.size());
-  ASSERT_EQ(0u, ss.clones[0].overlap[0].first);
-  ASSERT_EQ(128u, ss.clones[0].overlap[0].second);
-  ASSERT_EQ(256u, ss.clones[0].overlap[1].first);
-  ASSERT_EQ(128u, ss.clones[0].overlap[1].second);
-  ASSERT_EQ(512u, ss.clones[0].overlap[2].first);
-  ASSERT_EQ(128u, ss.clones[0].overlap[2].second);
-  ASSERT_EQ(768u, ss.clones[0].overlap[3].first);
-  ASSERT_EQ(128u, ss.clones[0].overlap[3].second);
-  ASSERT_EQ(1024u, ss.clones[0].overlap[4].first);
-  ASSERT_EQ(128u, ss.clones[0].overlap[4].second);
-  ASSERT_EQ(1152u, ss.clones[0].size);
-
-  ASSERT_EQ(my_snaps[2], ss.clones[1].cloneid);
-  ASSERT_EQ(1u, ss.clones[1].snaps.size());
-  ASSERT_EQ(my_snaps[2], ss.clones[1].snaps[0]);
-  ASSERT_EQ(4u, ss.clones[1].overlap.size());
-  ASSERT_EQ(0u, ss.clones[1].overlap[0].first);
-  ASSERT_EQ(128u, ss.clones[1].overlap[0].second);
-  ASSERT_EQ(256u, ss.clones[1].overlap[1].first);
-  ASSERT_EQ(256u, ss.clones[1].overlap[1].second);
-  ASSERT_EQ(768u, ss.clones[1].overlap[2].first);
-  ASSERT_EQ(256u, ss.clones[1].overlap[2].second);
-  ASSERT_EQ(1152u, ss.clones[1].overlap[3].first);
-  ASSERT_EQ(128u, ss.clones[1].overlap[3].second);
-  ASSERT_EQ(1280u, ss.clones[1].size);
-
-  ASSERT_EQ(head, ss.clones[2].cloneid);
-  ASSERT_EQ(0u, ss.clones[2].snaps.size());
-  ASSERT_EQ(0u, ss.clones[2].overlap.size());
-  ASSERT_EQ(1280u, ss.clones[2].size);
-
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_remove(my_snaps.back()));
-  my_snaps.pop_back();
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_remove(my_snaps.back()));
-  my_snaps.pop_back();
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_remove(my_snaps.back()));
-  my_snaps.pop_back();
-  readioctx.close();
-}
-
-TEST_F(LibRadosSnapshotsSelfManagedPP, Bug11677) {
-  std::vector<uint64_t> my_snaps;
-  my_snaps.push_back(-2);
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps.back()));
-  ::std::reverse(my_snaps.begin(), my_snaps.end());
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
-  ::std::reverse(my_snaps.begin(), my_snaps.end());
-
-  int bsize = 1<<20;
-  char *buf = (char *)new char[bsize];
-  memset(buf, 0xcc, bsize);
-  bufferlist bl1;
-  bl1.append(buf, bsize);
-  ASSERT_EQ(0, ioctx.write("foo", bl1, bsize, 0));
-
-  my_snaps.push_back(-2);
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps.back()));
-  ::std::reverse(my_snaps.begin(), my_snaps.end());
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
-  ::std::reverse(my_snaps.begin(), my_snaps.end());
-
-  std::unique_ptr<librados::ObjectWriteOperation> op(new librados::ObjectWriteOperation());
-  op->assert_exists();
-  op->remove();
-  ASSERT_EQ(0, ioctx.operate("foo", op.get()));
-
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_remove(my_snaps.back()));
-  my_snaps.pop_back();
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_remove(my_snaps.back()));
-  my_snaps.pop_back();
-  ioctx.snap_set_read(LIBRADOS_SNAP_HEAD);
-  delete[] buf;
-}
-
-TEST_F(LibRadosSnapshotsSelfManagedPP, OrderSnap) {
-  std::vector<uint64_t> my_snaps;
-  char buf[bufsize];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl;
-  bl.append(buf, sizeof(buf));
-
-  int flags = librados::OPERATION_ORDERSNAP;
-
-  my_snaps.push_back(-2);
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps.back()));
-  ::std::reverse(my_snaps.begin(), my_snaps.end());
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
-  ::std::reverse(my_snaps.begin(), my_snaps.end());
-  ObjectWriteOperation op1;
-  op1.write(0, bl);
-  librados::AioCompletion *comp1 = cluster.aio_create_completion();
-  ASSERT_EQ(0, ioctx.aio_operate("foo", comp1, &op1, flags));
-  ASSERT_EQ(0, comp1->wait_for_complete());
-  ASSERT_EQ(0, comp1->get_return_value());
-  comp1->release();
-
-  my_snaps.push_back(-2);
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps.back()));
-  ::std::reverse(my_snaps.begin(), my_snaps.end());
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
-  ::std::reverse(my_snaps.begin(), my_snaps.end());
-  ObjectWriteOperation op2;
-  op2.write(0, bl);
-  librados::AioCompletion *comp2 = cluster.aio_create_completion();
-  ASSERT_EQ(0, ioctx.aio_operate("foo", comp2, &op2, flags));
-  ASSERT_EQ(0, comp2->wait_for_complete());
-  ASSERT_EQ(0, comp2->get_return_value());
-  comp2->release();
-
-  my_snaps.pop_back();
-  ::std::reverse(my_snaps.begin(), my_snaps.end());
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
-  ::std::reverse(my_snaps.begin(), my_snaps.end());
-  ObjectWriteOperation op3;
-  op3.write(0, bl);
-  librados::AioCompletion *comp3 = cluster.aio_create_completion();
-  ASSERT_EQ(0, ioctx.aio_operate("foo", comp3, &op3, flags));
-  ASSERT_EQ(0, comp3->wait_for_complete());
-  ASSERT_EQ(-EOLDSNAPC, comp3->get_return_value());
-  comp3->release();
-
-  ObjectWriteOperation op4;
-  op4.write(0, bl);
-  librados::AioCompletion *comp4 = cluster.aio_create_completion();
-  ASSERT_EQ(0, ioctx.aio_operate("foo", comp4, &op4, 0));
-  ASSERT_EQ(0, comp4->wait_for_complete());
-  ASSERT_EQ(0, comp4->get_return_value());
-  comp4->release();
-}
-
 // EC testing
 TEST_F(LibRadosSnapshotsEC, SnapList) {
   char buf[bufsize];
@@ -610,22 +168,6 @@ TEST_F(LibRadosSnapshotsEC, SnapList) {
   EXPECT_EQ(0, rados_ioctx_snap_remove(ioctx, "snap1"));
 }
 
-TEST_F(LibRadosSnapshotsECPP, SnapListPP) {
-  char buf[bufsize];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
-  ASSERT_EQ(0, ioctx.snap_create("snap1"));
-  std::vector<snap_t> snaps;
-  EXPECT_EQ(0, ioctx.snap_list(&snaps));
-  EXPECT_EQ(1U, snaps.size());
-  snap_t rid;
-  EXPECT_EQ(0, ioctx.snap_lookup("snap1", &rid));
-  EXPECT_EQ(rid, snaps[0]);
-  EXPECT_EQ(0, ioctx.snap_remove("snap1"));
-}
-
 TEST_F(LibRadosSnapshotsEC, SnapRemove) {
   char buf[bufsize];
   memset(buf, 0xcc, sizeof(buf));
@@ -638,19 +180,6 @@ TEST_F(LibRadosSnapshotsEC, SnapRemove) {
   ASSERT_EQ(-ENOENT, rados_ioctx_snap_lookup(ioctx, "snap1", &rid));
 }
 
-TEST_F(LibRadosSnapshotsECPP, SnapRemovePP) {
-  char buf[bufsize];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
-  ASSERT_EQ(0, ioctx.snap_create("snap1"));
-  rados_snap_t rid;
-  ASSERT_EQ(0, ioctx.snap_lookup("snap1", &rid));
-  ASSERT_EQ(0, ioctx.snap_remove("snap1"));
-  ASSERT_EQ(-ENOENT, ioctx.snap_lookup("snap1", &rid));
-}
-
 TEST_F(LibRadosSnapshotsEC, Rollback) {
   char buf[bufsize];
   memset(buf, 0xcc, sizeof(buf));
@@ -666,25 +195,6 @@ TEST_F(LibRadosSnapshotsEC, Rollback) {
   EXPECT_EQ(0, rados_ioctx_snap_remove(ioctx, "snap1"));
 }
 
-TEST_F(LibRadosSnapshotsECPP, RollbackPP) {
-  char buf[bufsize];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
-  ASSERT_EQ(0, ioctx.snap_create("snap1"));
-  char buf2[sizeof(buf)];
-  memset(buf2, 0xdd, sizeof(buf2));
-  bufferlist bl2;
-  bl2.append(buf2, sizeof(buf2));
-  EXPECT_EQ(0, ioctx.write_full("foo", bl2));
-  EXPECT_EQ(0, ioctx.snap_rollback("foo", "snap1"));
-  bufferlist bl3;
-  EXPECT_EQ((int)sizeof(buf), ioctx.read("foo", bl3, sizeof(buf), 0));
-  EXPECT_EQ(0, memcmp(buf, bl3.c_str(), sizeof(buf)));
-  EXPECT_EQ(0, ioctx.snap_remove("snap1"));
-}
-
 TEST_F(LibRadosSnapshotsEC, SnapGetName) {
   char buf[bufsize];
   memset(buf, 0xcc, sizeof(buf));
@@ -702,24 +212,6 @@ TEST_F(LibRadosSnapshotsEC, SnapGetName) {
   EXPECT_EQ(0, rados_ioctx_snap_remove(ioctx, "snapfoo"));
 }
 
-TEST_F(LibRadosSnapshotsECPP, SnapGetNamePP) {
-  char buf[bufsize];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl;
-  bl.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
-  ASSERT_EQ(0, ioctx.snap_create("snapfoo"));
-  rados_snap_t rid;
-  EXPECT_EQ(0, ioctx.snap_lookup("snapfoo", &rid));
-  EXPECT_EQ(-ENOENT, ioctx.snap_lookup("snapbar", &rid));
-  std::string name;
-  EXPECT_EQ(0, ioctx.snap_get_name(rid, &name));
-  time_t snaptime;
-  EXPECT_EQ(0, ioctx.snap_get_stamp(rid, &snaptime));
-  EXPECT_EQ(0, strcmp(name.c_str(), "snapfoo"));
-  EXPECT_EQ(0, ioctx.snap_remove("snapfoo"));
-}
-
 TEST_F(LibRadosSnapshotsSelfManagedEC, Snap) {
   std::vector<uint64_t> my_snaps;
   my_snaps.push_back(-2);
@@ -807,171 +299,3 @@ TEST_F(LibRadosSnapshotsSelfManagedEC, Rollback) {
   delete[] buf2;
   delete[] buf3;
 }
-
-TEST_F(LibRadosSnapshotsSelfManagedECPP, SnapPP) {
-  std::vector<uint64_t> my_snaps;
-  my_snaps.push_back(-2);
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps.back()));
-  ::std::reverse(my_snaps.begin(), my_snaps.end());
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
-  ::std::reverse(my_snaps.begin(), my_snaps.end());
-  int bsize = alignment;
-  char *buf = (char *)new char[bsize];
-  memset(buf, 0xcc, bsize);
-  bufferlist bl1;
-  bl1.append(buf, bsize);
-  ASSERT_EQ(0, ioctx.write("foo", bl1, bsize, 0));
-
-  my_snaps.push_back(-2);
-  librados::AioCompletion *completion = cluster.aio_create_completion();
-  ioctx.aio_selfmanaged_snap_create(&my_snaps.back(), completion);
-  ASSERT_EQ(0, completion->wait_for_complete());
-  completion->release();
-  ::std::reverse(my_snaps.begin(), my_snaps.end());
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
-  ::std::reverse(my_snaps.begin(), my_snaps.end());
-  char *buf2 = (char *)new char[bsize];
-  memset(buf2, 0xdd, bsize);
-  bufferlist bl2;
-  bl2.append(buf2, bsize);
-  // Add another aligned buffer
-  ASSERT_EQ(0, ioctx.write("foo", bl2, bsize, bsize));
-
-  ioctx.snap_set_read(my_snaps[1]);
-  bufferlist bl3;
-  ASSERT_EQ(bsize, ioctx.read("foo", bl3, bsize*3, 0));
-  ASSERT_EQ(0, memcmp(bl3.c_str(), buf, bsize));
-
-  completion = cluster.aio_create_completion();
-  ioctx.aio_selfmanaged_snap_remove(my_snaps.back(), completion);
-  ASSERT_EQ(0, completion->wait_for_complete());
-  completion->release();
-  my_snaps.pop_back();
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_remove(my_snaps.back()));
-  my_snaps.pop_back();
-  ioctx.snap_set_read(LIBRADOS_SNAP_HEAD);
-  ASSERT_EQ(0, ioctx.remove("foo"));
-  delete[] buf;
-  delete[] buf2;
-}
-
-TEST_F(LibRadosSnapshotsSelfManagedECPP, RollbackPP) {
-  std::vector<uint64_t> my_snaps;
-  IoCtx readioctx;
-  ASSERT_EQ(0, cluster.ioctx_create(pool_name.c_str(), readioctx));
-  readioctx.set_namespace(nspace);
-  readioctx.snap_set_read(LIBRADOS_SNAP_DIR);
-
-  my_snaps.push_back(-2);
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps.back()));
-  ::std::reverse(my_snaps.begin(), my_snaps.end());
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
-  ::std::reverse(my_snaps.begin(), my_snaps.end());
-  int bsize = alignment;
-  char *buf = (char *)new char[bsize];
-  memset(buf, 0xcc, bsize);
-  bufferlist bl1;
-  bl1.append(buf, bsize);
-  //Write 3 consecutive buffers
-  ASSERT_EQ(0, ioctx.write("foo", bl1, bsize, 0));
-  ASSERT_EQ(0, ioctx.write("foo", bl1, bsize, bsize));
-  ASSERT_EQ(0, ioctx.write("foo", bl1, bsize, bsize*2));
-
-  snap_set_t ss;
-
-  snap_t head = SNAP_HEAD;
-  ASSERT_EQ(0, readioctx.list_snaps("foo", &ss));
-  ASSERT_EQ(1u, ss.clones.size());
-  ASSERT_EQ(head, ss.clones[0].cloneid);
-  ASSERT_EQ(0u, ss.clones[0].snaps.size());
-  ASSERT_EQ(0u, ss.clones[0].overlap.size());
-  ASSERT_EQ((unsigned)(bsize*3), ss.clones[0].size);
-
-  my_snaps.push_back(-2);
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps.back()));
-  ::std::reverse(my_snaps.begin(), my_snaps.end());
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
-  ::std::reverse(my_snaps.begin(), my_snaps.end());
-  char *buf2 = (char *)new char[bsize];
-  memset(buf2, 0xdd, bsize);
-  bufferlist bl2;
-  bl2.append(buf2, bsize);
-  //Change the middle buffer
-  //ASSERT_EQ(0, ioctx.write("foo", bl2, sizeof(buf2), bufsize));
-  //Add another after
-  ASSERT_EQ(0, ioctx.write("foo", bl2, bsize, bsize*3));
-
-  ASSERT_EQ(-EINVAL, ioctx.list_snaps("foo", &ss));
-  ObjectReadOperation o;
-  o.list_snaps(&ss, NULL);
-  ASSERT_EQ(-EINVAL, ioctx.operate("foo", &o, NULL));
-
-  ASSERT_EQ(0, readioctx.list_snaps("foo", &ss));
-  ASSERT_EQ(2u, ss.clones.size());
-  ASSERT_EQ(my_snaps[1], ss.clones[0].cloneid);
-  ASSERT_EQ(1u, ss.clones[0].snaps.size());
-  ASSERT_EQ(my_snaps[1], ss.clones[0].snaps[0]);
-  ASSERT_EQ(1u, ss.clones[0].overlap.size());
-  ASSERT_EQ(0u, ss.clones[0].overlap[0].first);
-  ASSERT_EQ((unsigned)bsize*3, ss.clones[0].overlap[0].second);
-  ASSERT_EQ((unsigned)bsize*3, ss.clones[0].size);
-  ASSERT_EQ(head, ss.clones[1].cloneid);
-  ASSERT_EQ(0u, ss.clones[1].snaps.size());
-  ASSERT_EQ(0u, ss.clones[1].overlap.size());
-  ASSERT_EQ((unsigned)bsize*4, ss.clones[1].size);
-
-  ioctx.selfmanaged_snap_rollback("foo", my_snaps[1]);
-
-  bufferlist bl3;
-  ASSERT_EQ(bsize, ioctx.read("foo", bl3, bsize, 0));
-  ASSERT_EQ(0, memcmp(bl3.c_str(), buf, bsize));
-  ASSERT_EQ(bsize, ioctx.read("foo", bl3, bsize, bsize));
-  ASSERT_EQ(0, memcmp(bl3.c_str(), buf, bsize));
-  ASSERT_EQ(bsize, ioctx.read("foo", bl3, bsize, bsize*2));
-  ASSERT_EQ(0, memcmp(bl3.c_str(), buf, bsize));
-  ASSERT_EQ(0, ioctx.read("foo", bl3, bsize, bsize*3));
-
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_remove(my_snaps.back()));
-  my_snaps.pop_back();
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_remove(my_snaps.back()));
-  my_snaps.pop_back();
-  readioctx.close();
-
-  delete[] buf;
-  delete[] buf2;
-}
-
-TEST_F(LibRadosSnapshotsSelfManagedECPP, Bug11677) {
-  std::vector<uint64_t> my_snaps;
-  my_snaps.push_back(-2);
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps.back()));
-  ::std::reverse(my_snaps.begin(), my_snaps.end());
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
-  ::std::reverse(my_snaps.begin(), my_snaps.end());
-
-  int bsize = alignment;
-  char *buf = (char *)new char[bsize];
-  memset(buf, 0xcc, bsize);
-  bufferlist bl1;
-  bl1.append(buf, bsize);
-  ASSERT_EQ(0, ioctx.write("foo", bl1, bsize, 0));
-
-  my_snaps.push_back(-2);
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps.back()));
-  ::std::reverse(my_snaps.begin(), my_snaps.end());
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
-  ::std::reverse(my_snaps.begin(), my_snaps.end());
-
-  std::unique_ptr<librados::ObjectWriteOperation> op(new librados::ObjectWriteOperation());
-  op->assert_exists();
-  op->remove();
-  ASSERT_EQ(0, ioctx.operate("foo", op.get()));
-
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_remove(my_snaps.back()));
-  my_snaps.pop_back();
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_remove(my_snaps.back()));
-  my_snaps.pop_back();
-  ioctx.snap_set_read(LIBRADOS_SNAP_HEAD);
-  delete[] buf;
-}
-
diff --git a/src/test/librados/snapshots_cxx.cc b/src/test/librados/snapshots_cxx.cc
new file mode 100644 (file)
index 0000000..be3087f
--- /dev/null
@@ -0,0 +1,689 @@
+#include <algorithm>
+#include <errno.h>
+#include <string>
+
+#include "gtest/gtest.h"
+
+#include "include/rados.h"
+#include "include/rados/librados.hpp"
+#include "test/librados/test_cxx.h"
+#include "test/librados/testcase_cxx.h"
+
+using namespace librados;
+
+typedef RadosTestPP LibRadosSnapshotsPP;
+typedef RadosTestPP LibRadosSnapshotsSelfManagedPP;
+typedef RadosTestECPP LibRadosSnapshotsECPP;
+typedef RadosTestECPP LibRadosSnapshotsSelfManagedECPP;
+
+const int bufsize = 128;
+
+TEST_F(LibRadosSnapshotsPP, SnapListPP) {
+  char buf[bufsize];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+  ASSERT_FALSE(cluster.get_pool_is_selfmanaged_snaps_mode(pool_name));
+  ASSERT_EQ(0, ioctx.snap_create("snap1"));
+  ASSERT_FALSE(cluster.get_pool_is_selfmanaged_snaps_mode(pool_name));
+  std::vector<snap_t> snaps;
+  EXPECT_EQ(0, ioctx.snap_list(&snaps));
+  EXPECT_EQ(1U, snaps.size());
+  snap_t rid;
+  EXPECT_EQ(0, ioctx.snap_lookup("snap1", &rid));
+  EXPECT_EQ(rid, snaps[0]);
+  EXPECT_EQ(0, ioctx.snap_remove("snap1"));
+  ASSERT_FALSE(cluster.get_pool_is_selfmanaged_snaps_mode(pool_name));
+}
+
+TEST_F(LibRadosSnapshotsPP, SnapRemovePP) {
+  char buf[bufsize];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+  ASSERT_EQ(0, ioctx.snap_create("snap1"));
+  rados_snap_t rid;
+  ASSERT_EQ(0, ioctx.snap_lookup("snap1", &rid));
+  ASSERT_EQ(0, ioctx.snap_remove("snap1"));
+  ASSERT_EQ(-ENOENT, ioctx.snap_lookup("snap1", &rid));
+}
+
+TEST_F(LibRadosSnapshotsPP, RollbackPP) {
+  char buf[bufsize];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+  ASSERT_EQ(0, ioctx.snap_create("snap1"));
+  char buf2[sizeof(buf)];
+  memset(buf2, 0xdd, sizeof(buf2));
+  bufferlist bl2;
+  bl2.append(buf2, sizeof(buf2));
+  EXPECT_EQ(0, ioctx.write_full("foo", bl2));
+  EXPECT_EQ(0, ioctx.snap_rollback("foo", "snap1"));
+  bufferlist bl3;
+  EXPECT_EQ((int)sizeof(buf), ioctx.read("foo", bl3, sizeof(buf), 0));
+  EXPECT_EQ(0, memcmp(buf, bl3.c_str(), sizeof(buf)));
+  EXPECT_EQ(0, ioctx.snap_remove("snap1"));
+}
+
+TEST_F(LibRadosSnapshotsPP, SnapGetNamePP) {
+  char buf[bufsize];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl;
+  bl.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
+  ASSERT_EQ(0, ioctx.snap_create("snapfoo"));
+  rados_snap_t rid;
+  EXPECT_EQ(0, ioctx.snap_lookup("snapfoo", &rid));
+  EXPECT_EQ(-ENOENT, ioctx.snap_lookup("snapbar", &rid));
+  std::string name;
+  EXPECT_EQ(0, ioctx.snap_get_name(rid, &name));
+  time_t snaptime;
+  EXPECT_EQ(0, ioctx.snap_get_stamp(rid, &snaptime));
+  EXPECT_EQ(0, strcmp(name.c_str(), "snapfoo"));
+  EXPECT_EQ(0, ioctx.snap_remove("snapfoo"));
+}
+
+TEST_F(LibRadosSnapshotsPP, SnapCreateRemovePP) {
+  // reproduces http://tracker.ceph.com/issues/10262
+  bufferlist bl;
+  bl.append("foo");
+  ASSERT_EQ(0, ioctx.write("foo", bl, bl.length(), 0));
+  ASSERT_EQ(0, ioctx.snap_create("snapfoo"));
+  ASSERT_EQ(0, ioctx.remove("foo"));
+  ASSERT_EQ(0, ioctx.snap_create("snapbar"));
+
+  std::unique_ptr<librados::ObjectWriteOperation> op(new librados::ObjectWriteOperation());
+  op->create(false);
+  op->remove();
+  ASSERT_EQ(0, ioctx.operate("foo", op.get()));
+
+  EXPECT_EQ(0, ioctx.snap_remove("snapfoo"));
+  EXPECT_EQ(0, ioctx.snap_remove("snapbar"));
+}
+
+TEST_F(LibRadosSnapshotsSelfManagedPP, SnapPP) {
+  std::vector<uint64_t> my_snaps;
+  my_snaps.push_back(-2);
+  ASSERT_FALSE(cluster.get_pool_is_selfmanaged_snaps_mode(pool_name));
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps.back()));
+  ASSERT_TRUE(cluster.get_pool_is_selfmanaged_snaps_mode(pool_name));
+  ::std::reverse(my_snaps.begin(), my_snaps.end()); 
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
+  ::std::reverse(my_snaps.begin(), my_snaps.end());
+  char buf[bufsize];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+
+  my_snaps.push_back(-2);
+  librados::AioCompletion *completion = cluster.aio_create_completion();
+  ioctx.aio_selfmanaged_snap_create(&my_snaps.back(), completion);
+  ASSERT_EQ(0, completion->wait_for_complete());
+  completion->release();
+  ::std::reverse(my_snaps.begin(), my_snaps.end());
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
+  ::std::reverse(my_snaps.begin(), my_snaps.end());
+  char buf2[sizeof(buf)];
+  memset(buf2, 0xdd, sizeof(buf2));
+  bufferlist bl2;
+  bl2.append(buf2, sizeof(buf2));
+  ASSERT_EQ(0, ioctx.write("foo", bl2, sizeof(buf2), 0));
+
+  ioctx.snap_set_read(my_snaps[1]);
+  bufferlist bl3;
+  ASSERT_EQ((int)sizeof(buf), ioctx.read("foo", bl3, sizeof(buf), 0));
+  ASSERT_EQ(0, memcmp(bl3.c_str(), buf, sizeof(buf)));
+
+  completion = cluster.aio_create_completion();
+  ioctx.aio_selfmanaged_snap_remove(my_snaps.back(), completion);
+  ASSERT_EQ(0, completion->wait_for_complete());
+  completion->release();
+  my_snaps.pop_back();
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_remove(my_snaps.back()));
+  my_snaps.pop_back();
+  ioctx.snap_set_read(LIBRADOS_SNAP_HEAD);
+  ASSERT_TRUE(cluster.get_pool_is_selfmanaged_snaps_mode(pool_name));
+  ASSERT_EQ(0, ioctx.remove("foo"));
+}
+
+TEST_F(LibRadosSnapshotsSelfManagedPP, RollbackPP) {
+  std::vector<uint64_t> my_snaps;
+  IoCtx readioctx;
+  ASSERT_EQ(0, cluster.ioctx_create(pool_name.c_str(), readioctx));
+  readioctx.set_namespace(nspace);
+  readioctx.snap_set_read(LIBRADOS_SNAP_DIR);
+
+  my_snaps.push_back(-2);
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps.back()));
+  ::std::reverse(my_snaps.begin(), my_snaps.end());
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
+  ::std::reverse(my_snaps.begin(), my_snaps.end());
+  char buf[bufsize];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  //Write 3 consecutive buffers
+  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), bufsize));
+  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), bufsize*2));
+
+  snap_set_t ss;
+
+  snap_t head = SNAP_HEAD;
+  ASSERT_EQ(0, readioctx.list_snaps("foo", &ss));
+  ASSERT_EQ(1u, ss.clones.size());
+  ASSERT_EQ(head, ss.clones[0].cloneid);
+  ASSERT_EQ(0u, ss.clones[0].snaps.size());
+  ASSERT_EQ(0u, ss.clones[0].overlap.size());
+  ASSERT_EQ(384u, ss.clones[0].size);
+
+  my_snaps.push_back(-2);
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps.back()));
+  ::std::reverse(my_snaps.begin(), my_snaps.end());
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
+  ::std::reverse(my_snaps.begin(), my_snaps.end());
+  char buf2[sizeof(buf)];
+  memset(buf2, 0xdd, sizeof(buf2));
+  bufferlist bl2;
+  bl2.append(buf2, sizeof(buf2));
+  //Change the middle buffer
+  ASSERT_EQ(0, ioctx.write("foo", bl2, sizeof(buf2), bufsize));
+  //Add another after
+  ASSERT_EQ(0, ioctx.write("foo", bl2, sizeof(buf2), bufsize*3));
+
+  ASSERT_EQ(-EINVAL, ioctx.list_snaps("foo", &ss));
+  ObjectReadOperation o;
+  o.list_snaps(&ss, NULL);
+  ASSERT_EQ(-EINVAL, ioctx.operate("foo", &o, NULL));
+
+  ASSERT_EQ(0, readioctx.list_snaps("foo", &ss));
+  ASSERT_EQ(2u, ss.clones.size());
+  ASSERT_EQ(my_snaps[1], ss.clones[0].cloneid);
+  ASSERT_EQ(1u, ss.clones[0].snaps.size());
+  ASSERT_EQ(my_snaps[1], ss.clones[0].snaps[0]);
+  ASSERT_EQ(2u, ss.clones[0].overlap.size());
+  ASSERT_EQ(0u, ss.clones[0].overlap[0].first);
+  ASSERT_EQ(128u, ss.clones[0].overlap[0].second);
+  ASSERT_EQ(256u, ss.clones[0].overlap[1].first);
+  ASSERT_EQ(128u, ss.clones[0].overlap[1].second);
+  ASSERT_EQ(384u, ss.clones[0].size);
+  ASSERT_EQ(head, ss.clones[1].cloneid);
+  ASSERT_EQ(0u, ss.clones[1].snaps.size());
+  ASSERT_EQ(0u, ss.clones[1].overlap.size());
+  ASSERT_EQ(512u, ss.clones[1].size);
+
+  ioctx.selfmanaged_snap_rollback("foo", my_snaps[1]);
+
+  bufferlist bl3;
+  ASSERT_EQ((int)sizeof(buf), ioctx.read("foo", bl3, sizeof(buf), 0));
+  ASSERT_EQ(0, memcmp(bl3.c_str(), buf, sizeof(buf)));
+  ASSERT_EQ((int)sizeof(buf), ioctx.read("foo", bl3, sizeof(buf), bufsize));
+  ASSERT_EQ(0, memcmp(bl3.c_str(), buf, sizeof(buf)));
+  ASSERT_EQ((int)sizeof(buf), ioctx.read("foo", bl3, sizeof(buf), bufsize*2));
+  ASSERT_EQ(0, memcmp(bl3.c_str(), buf, sizeof(buf)));
+  ASSERT_EQ((int)0, ioctx.read("foo", bl3, sizeof(buf), bufsize*3));
+
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_remove(my_snaps.back()));
+  my_snaps.pop_back();
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_remove(my_snaps.back()));
+  my_snaps.pop_back();
+  readioctx.close();
+}
+
+TEST_F(LibRadosSnapshotsSelfManagedPP, SnapOverlapPP) {
+  std::vector<uint64_t> my_snaps;
+  IoCtx readioctx;
+  ASSERT_EQ(0, cluster.ioctx_create(pool_name.c_str(), readioctx));
+  readioctx.set_namespace(nspace);
+  readioctx.snap_set_read(LIBRADOS_SNAP_DIR);
+
+  my_snaps.push_back(-2);
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps.back()));
+  ::std::reverse(my_snaps.begin(), my_snaps.end());
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
+  ::std::reverse(my_snaps.begin(), my_snaps.end());
+  char buf[bufsize];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), bufsize*2));
+  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), bufsize*4));
+  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), bufsize*6));
+  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), bufsize*8));
+
+  snap_set_t ss;
+  snap_t head = SNAP_HEAD;
+  ASSERT_EQ(0, readioctx.list_snaps("foo", &ss));
+  ASSERT_EQ(1u, ss.clones.size());
+  ASSERT_EQ(head, ss.clones[0].cloneid);
+  ASSERT_EQ(0u, ss.clones[0].snaps.size());
+  ASSERT_EQ(0u, ss.clones[0].overlap.size());
+  ASSERT_EQ(1152u, ss.clones[0].size);
+
+  my_snaps.push_back(-2);
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps.back()));
+  ::std::reverse(my_snaps.begin(), my_snaps.end());
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
+  ::std::reverse(my_snaps.begin(), my_snaps.end());
+  char buf2[sizeof(buf)];
+  memset(buf2, 0xdd, sizeof(buf2));
+  bufferlist bl2;
+  bl2.append(buf2, sizeof(buf2));
+  ASSERT_EQ(0, ioctx.write("foo", bl2, sizeof(buf2), bufsize*1));
+  ASSERT_EQ(0, ioctx.write("foo", bl2, sizeof(buf2), bufsize*3));
+  ASSERT_EQ(0, ioctx.write("foo", bl2, sizeof(buf2), bufsize*5));
+  ASSERT_EQ(0, ioctx.write("foo", bl2, sizeof(buf2), bufsize*7));
+  ASSERT_EQ(0, ioctx.write("foo", bl2, sizeof(buf2), bufsize*9));
+
+  ASSERT_EQ(0, readioctx.list_snaps("foo", &ss));
+  ASSERT_EQ(2u, ss.clones.size());
+  ASSERT_EQ(my_snaps[1], ss.clones[0].cloneid);
+  ASSERT_EQ(1u, ss.clones[0].snaps.size());
+  ASSERT_EQ(my_snaps[1], ss.clones[0].snaps[0]);
+  ASSERT_EQ(5u, ss.clones[0].overlap.size());
+  ASSERT_EQ(0u, ss.clones[0].overlap[0].first);
+  ASSERT_EQ(128u, ss.clones[0].overlap[0].second);
+  ASSERT_EQ(256u, ss.clones[0].overlap[1].first);
+  ASSERT_EQ(128u, ss.clones[0].overlap[1].second);
+  ASSERT_EQ(512u, ss.clones[0].overlap[2].first);
+  ASSERT_EQ(128u, ss.clones[0].overlap[2].second);
+  ASSERT_EQ(768u, ss.clones[0].overlap[3].first);
+  ASSERT_EQ(128u, ss.clones[0].overlap[3].second);
+  ASSERT_EQ(1024u, ss.clones[0].overlap[4].first);
+  ASSERT_EQ(128u, ss.clones[0].overlap[4].second);
+  ASSERT_EQ(1152u, ss.clones[0].size);
+  ASSERT_EQ(head, ss.clones[1].cloneid);
+  ASSERT_EQ(0u, ss.clones[1].snaps.size());
+  ASSERT_EQ(0u, ss.clones[1].overlap.size());
+  ASSERT_EQ(1280u, ss.clones[1].size);
+
+  my_snaps.push_back(-2);
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps.back()));
+  ::std::reverse(my_snaps.begin(), my_snaps.end());
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
+  ::std::reverse(my_snaps.begin(), my_snaps.end());
+
+  char buf3[sizeof(buf)];
+  memset(buf3, 0xee, sizeof(buf3));
+  bufferlist bl4;
+  bl4.append(buf3, sizeof(buf3));
+  ASSERT_EQ(0, ioctx.write("foo", bl2, sizeof(buf3), bufsize*1));
+  ASSERT_EQ(0, ioctx.write("foo", bl2, sizeof(buf3), bufsize*4));
+  ASSERT_EQ(0, ioctx.write("foo", bl2, sizeof(buf3), bufsize*5));
+  ASSERT_EQ(0, ioctx.write("foo", bl2, sizeof(buf3), bufsize*8));
+
+  ASSERT_EQ(0, readioctx.list_snaps("foo", &ss));
+  ASSERT_EQ(3u, ss.clones.size());
+  ASSERT_EQ(my_snaps[1], ss.clones[0].cloneid);
+  ASSERT_EQ(1u, ss.clones[0].snaps.size());
+  ASSERT_EQ(my_snaps[1], ss.clones[0].snaps[0]);
+  ASSERT_EQ(5u, ss.clones[0].overlap.size());
+  ASSERT_EQ(0u, ss.clones[0].overlap[0].first);
+  ASSERT_EQ(128u, ss.clones[0].overlap[0].second);
+  ASSERT_EQ(256u, ss.clones[0].overlap[1].first);
+  ASSERT_EQ(128u, ss.clones[0].overlap[1].second);
+  ASSERT_EQ(512u, ss.clones[0].overlap[2].first);
+  ASSERT_EQ(128u, ss.clones[0].overlap[2].second);
+  ASSERT_EQ(768u, ss.clones[0].overlap[3].first);
+  ASSERT_EQ(128u, ss.clones[0].overlap[3].second);
+  ASSERT_EQ(1024u, ss.clones[0].overlap[4].first);
+  ASSERT_EQ(128u, ss.clones[0].overlap[4].second);
+  ASSERT_EQ(1152u, ss.clones[0].size);
+
+  ASSERT_EQ(my_snaps[2], ss.clones[1].cloneid);
+  ASSERT_EQ(1u, ss.clones[1].snaps.size());
+  ASSERT_EQ(my_snaps[2], ss.clones[1].snaps[0]);
+  ASSERT_EQ(4u, ss.clones[1].overlap.size());
+  ASSERT_EQ(0u, ss.clones[1].overlap[0].first);
+  ASSERT_EQ(128u, ss.clones[1].overlap[0].second);
+  ASSERT_EQ(256u, ss.clones[1].overlap[1].first);
+  ASSERT_EQ(256u, ss.clones[1].overlap[1].second);
+  ASSERT_EQ(768u, ss.clones[1].overlap[2].first);
+  ASSERT_EQ(256u, ss.clones[1].overlap[2].second);
+  ASSERT_EQ(1152u, ss.clones[1].overlap[3].first);
+  ASSERT_EQ(128u, ss.clones[1].overlap[3].second);
+  ASSERT_EQ(1280u, ss.clones[1].size);
+
+  ASSERT_EQ(head, ss.clones[2].cloneid);
+  ASSERT_EQ(0u, ss.clones[2].snaps.size());
+  ASSERT_EQ(0u, ss.clones[2].overlap.size());
+  ASSERT_EQ(1280u, ss.clones[2].size);
+
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_remove(my_snaps.back()));
+  my_snaps.pop_back();
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_remove(my_snaps.back()));
+  my_snaps.pop_back();
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_remove(my_snaps.back()));
+  my_snaps.pop_back();
+  readioctx.close();
+}
+
+TEST_F(LibRadosSnapshotsSelfManagedPP, Bug11677) {
+  std::vector<uint64_t> my_snaps;
+  my_snaps.push_back(-2);
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps.back()));
+  ::std::reverse(my_snaps.begin(), my_snaps.end());
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
+  ::std::reverse(my_snaps.begin(), my_snaps.end());
+
+  int bsize = 1<<20;
+  char *buf = (char *)new char[bsize];
+  memset(buf, 0xcc, bsize);
+  bufferlist bl1;
+  bl1.append(buf, bsize);
+  ASSERT_EQ(0, ioctx.write("foo", bl1, bsize, 0));
+
+  my_snaps.push_back(-2);
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps.back()));
+  ::std::reverse(my_snaps.begin(), my_snaps.end());
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
+  ::std::reverse(my_snaps.begin(), my_snaps.end());
+
+  std::unique_ptr<librados::ObjectWriteOperation> op(new librados::ObjectWriteOperation());
+  op->assert_exists();
+  op->remove();
+  ASSERT_EQ(0, ioctx.operate("foo", op.get()));
+
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_remove(my_snaps.back()));
+  my_snaps.pop_back();
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_remove(my_snaps.back()));
+  my_snaps.pop_back();
+  ioctx.snap_set_read(LIBRADOS_SNAP_HEAD);
+  delete[] buf;
+}
+
+TEST_F(LibRadosSnapshotsSelfManagedPP, OrderSnap) {
+  std::vector<uint64_t> my_snaps;
+  char buf[bufsize];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl;
+  bl.append(buf, sizeof(buf));
+
+  int flags = librados::OPERATION_ORDERSNAP;
+
+  my_snaps.push_back(-2);
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps.back()));
+  ::std::reverse(my_snaps.begin(), my_snaps.end());
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
+  ::std::reverse(my_snaps.begin(), my_snaps.end());
+  ObjectWriteOperation op1;
+  op1.write(0, bl);
+  librados::AioCompletion *comp1 = cluster.aio_create_completion();
+  ASSERT_EQ(0, ioctx.aio_operate("foo", comp1, &op1, flags));
+  ASSERT_EQ(0, comp1->wait_for_complete());
+  ASSERT_EQ(0, comp1->get_return_value());
+  comp1->release();
+
+  my_snaps.push_back(-2);
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps.back()));
+  ::std::reverse(my_snaps.begin(), my_snaps.end());
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
+  ::std::reverse(my_snaps.begin(), my_snaps.end());
+  ObjectWriteOperation op2;
+  op2.write(0, bl);
+  librados::AioCompletion *comp2 = cluster.aio_create_completion();
+  ASSERT_EQ(0, ioctx.aio_operate("foo", comp2, &op2, flags));
+  ASSERT_EQ(0, comp2->wait_for_complete());
+  ASSERT_EQ(0, comp2->get_return_value());
+  comp2->release();
+
+  my_snaps.pop_back();
+  ::std::reverse(my_snaps.begin(), my_snaps.end());
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
+  ::std::reverse(my_snaps.begin(), my_snaps.end());
+  ObjectWriteOperation op3;
+  op3.write(0, bl);
+  librados::AioCompletion *comp3 = cluster.aio_create_completion();
+  ASSERT_EQ(0, ioctx.aio_operate("foo", comp3, &op3, flags));
+  ASSERT_EQ(0, comp3->wait_for_complete());
+  ASSERT_EQ(-EOLDSNAPC, comp3->get_return_value());
+  comp3->release();
+
+  ObjectWriteOperation op4;
+  op4.write(0, bl);
+  librados::AioCompletion *comp4 = cluster.aio_create_completion();
+  ASSERT_EQ(0, ioctx.aio_operate("foo", comp4, &op4, 0));
+  ASSERT_EQ(0, comp4->wait_for_complete());
+  ASSERT_EQ(0, comp4->get_return_value());
+  comp4->release();
+}
+
+// EC testing
+TEST_F(LibRadosSnapshotsECPP, SnapListPP) {
+  char buf[bufsize];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+  ASSERT_EQ(0, ioctx.snap_create("snap1"));
+  std::vector<snap_t> snaps;
+  EXPECT_EQ(0, ioctx.snap_list(&snaps));
+  EXPECT_EQ(1U, snaps.size());
+  snap_t rid;
+  EXPECT_EQ(0, ioctx.snap_lookup("snap1", &rid));
+  EXPECT_EQ(rid, snaps[0]);
+  EXPECT_EQ(0, ioctx.snap_remove("snap1"));
+}
+
+TEST_F(LibRadosSnapshotsECPP, SnapRemovePP) {
+  char buf[bufsize];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+  ASSERT_EQ(0, ioctx.snap_create("snap1"));
+  rados_snap_t rid;
+  ASSERT_EQ(0, ioctx.snap_lookup("snap1", &rid));
+  ASSERT_EQ(0, ioctx.snap_remove("snap1"));
+  ASSERT_EQ(-ENOENT, ioctx.snap_lookup("snap1", &rid));
+}
+
+TEST_F(LibRadosSnapshotsECPP, RollbackPP) {
+  char buf[bufsize];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+  ASSERT_EQ(0, ioctx.snap_create("snap1"));
+  char buf2[sizeof(buf)];
+  memset(buf2, 0xdd, sizeof(buf2));
+  bufferlist bl2;
+  bl2.append(buf2, sizeof(buf2));
+  EXPECT_EQ(0, ioctx.write_full("foo", bl2));
+  EXPECT_EQ(0, ioctx.snap_rollback("foo", "snap1"));
+  bufferlist bl3;
+  EXPECT_EQ((int)sizeof(buf), ioctx.read("foo", bl3, sizeof(buf), 0));
+  EXPECT_EQ(0, memcmp(buf, bl3.c_str(), sizeof(buf)));
+  EXPECT_EQ(0, ioctx.snap_remove("snap1"));
+}
+
+TEST_F(LibRadosSnapshotsECPP, SnapGetNamePP) {
+  char buf[bufsize];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl;
+  bl.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
+  ASSERT_EQ(0, ioctx.snap_create("snapfoo"));
+  rados_snap_t rid;
+  EXPECT_EQ(0, ioctx.snap_lookup("snapfoo", &rid));
+  EXPECT_EQ(-ENOENT, ioctx.snap_lookup("snapbar", &rid));
+  std::string name;
+  EXPECT_EQ(0, ioctx.snap_get_name(rid, &name));
+  time_t snaptime;
+  EXPECT_EQ(0, ioctx.snap_get_stamp(rid, &snaptime));
+  EXPECT_EQ(0, strcmp(name.c_str(), "snapfoo"));
+  EXPECT_EQ(0, ioctx.snap_remove("snapfoo"));
+}
+
+TEST_F(LibRadosSnapshotsSelfManagedECPP, SnapPP) {
+  std::vector<uint64_t> my_snaps;
+  my_snaps.push_back(-2);
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps.back()));
+  ::std::reverse(my_snaps.begin(), my_snaps.end());
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
+  ::std::reverse(my_snaps.begin(), my_snaps.end());
+  int bsize = alignment;
+  char *buf = (char *)new char[bsize];
+  memset(buf, 0xcc, bsize);
+  bufferlist bl1;
+  bl1.append(buf, bsize);
+  ASSERT_EQ(0, ioctx.write("foo", bl1, bsize, 0));
+
+  my_snaps.push_back(-2);
+  librados::AioCompletion *completion = cluster.aio_create_completion();
+  ioctx.aio_selfmanaged_snap_create(&my_snaps.back(), completion);
+  ASSERT_EQ(0, completion->wait_for_complete());
+  completion->release();
+  ::std::reverse(my_snaps.begin(), my_snaps.end());
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
+  ::std::reverse(my_snaps.begin(), my_snaps.end());
+  char *buf2 = (char *)new char[bsize];
+  memset(buf2, 0xdd, bsize);
+  bufferlist bl2;
+  bl2.append(buf2, bsize);
+  // Add another aligned buffer
+  ASSERT_EQ(0, ioctx.write("foo", bl2, bsize, bsize));
+
+  ioctx.snap_set_read(my_snaps[1]);
+  bufferlist bl3;
+  ASSERT_EQ(bsize, ioctx.read("foo", bl3, bsize*3, 0));
+  ASSERT_EQ(0, memcmp(bl3.c_str(), buf, bsize));
+
+  completion = cluster.aio_create_completion();
+  ioctx.aio_selfmanaged_snap_remove(my_snaps.back(), completion);
+  ASSERT_EQ(0, completion->wait_for_complete());
+  completion->release();
+  my_snaps.pop_back();
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_remove(my_snaps.back()));
+  my_snaps.pop_back();
+  ioctx.snap_set_read(LIBRADOS_SNAP_HEAD);
+  ASSERT_EQ(0, ioctx.remove("foo"));
+  delete[] buf;
+  delete[] buf2;
+}
+
+TEST_F(LibRadosSnapshotsSelfManagedECPP, RollbackPP) {
+  std::vector<uint64_t> my_snaps;
+  IoCtx readioctx;
+  ASSERT_EQ(0, cluster.ioctx_create(pool_name.c_str(), readioctx));
+  readioctx.set_namespace(nspace);
+  readioctx.snap_set_read(LIBRADOS_SNAP_DIR);
+
+  my_snaps.push_back(-2);
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps.back()));
+  ::std::reverse(my_snaps.begin(), my_snaps.end());
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
+  ::std::reverse(my_snaps.begin(), my_snaps.end());
+  int bsize = alignment;
+  char *buf = (char *)new char[bsize];
+  memset(buf, 0xcc, bsize);
+  bufferlist bl1;
+  bl1.append(buf, bsize);
+  //Write 3 consecutive buffers
+  ASSERT_EQ(0, ioctx.write("foo", bl1, bsize, 0));
+  ASSERT_EQ(0, ioctx.write("foo", bl1, bsize, bsize));
+  ASSERT_EQ(0, ioctx.write("foo", bl1, bsize, bsize*2));
+
+  snap_set_t ss;
+
+  snap_t head = SNAP_HEAD;
+  ASSERT_EQ(0, readioctx.list_snaps("foo", &ss));
+  ASSERT_EQ(1u, ss.clones.size());
+  ASSERT_EQ(head, ss.clones[0].cloneid);
+  ASSERT_EQ(0u, ss.clones[0].snaps.size());
+  ASSERT_EQ(0u, ss.clones[0].overlap.size());
+  ASSERT_EQ((unsigned)(bsize*3), ss.clones[0].size);
+
+  my_snaps.push_back(-2);
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps.back()));
+  ::std::reverse(my_snaps.begin(), my_snaps.end());
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
+  ::std::reverse(my_snaps.begin(), my_snaps.end());
+  char *buf2 = (char *)new char[bsize];
+  memset(buf2, 0xdd, bsize);
+  bufferlist bl2;
+  bl2.append(buf2, bsize);
+  //Change the middle buffer
+  //ASSERT_EQ(0, ioctx.write("foo", bl2, sizeof(buf2), bufsize));
+  //Add another after
+  ASSERT_EQ(0, ioctx.write("foo", bl2, bsize, bsize*3));
+
+  ASSERT_EQ(-EINVAL, ioctx.list_snaps("foo", &ss));
+  ObjectReadOperation o;
+  o.list_snaps(&ss, NULL);
+  ASSERT_EQ(-EINVAL, ioctx.operate("foo", &o, NULL));
+
+  ASSERT_EQ(0, readioctx.list_snaps("foo", &ss));
+  ASSERT_EQ(2u, ss.clones.size());
+  ASSERT_EQ(my_snaps[1], ss.clones[0].cloneid);
+  ASSERT_EQ(1u, ss.clones[0].snaps.size());
+  ASSERT_EQ(my_snaps[1], ss.clones[0].snaps[0]);
+  ASSERT_EQ(1u, ss.clones[0].overlap.size());
+  ASSERT_EQ(0u, ss.clones[0].overlap[0].first);
+  ASSERT_EQ((unsigned)bsize*3, ss.clones[0].overlap[0].second);
+  ASSERT_EQ((unsigned)bsize*3, ss.clones[0].size);
+  ASSERT_EQ(head, ss.clones[1].cloneid);
+  ASSERT_EQ(0u, ss.clones[1].snaps.size());
+  ASSERT_EQ(0u, ss.clones[1].overlap.size());
+  ASSERT_EQ((unsigned)bsize*4, ss.clones[1].size);
+
+  ioctx.selfmanaged_snap_rollback("foo", my_snaps[1]);
+
+  bufferlist bl3;
+  ASSERT_EQ(bsize, ioctx.read("foo", bl3, bsize, 0));
+  ASSERT_EQ(0, memcmp(bl3.c_str(), buf, bsize));
+  ASSERT_EQ(bsize, ioctx.read("foo", bl3, bsize, bsize));
+  ASSERT_EQ(0, memcmp(bl3.c_str(), buf, bsize));
+  ASSERT_EQ(bsize, ioctx.read("foo", bl3, bsize, bsize*2));
+  ASSERT_EQ(0, memcmp(bl3.c_str(), buf, bsize));
+  ASSERT_EQ(0, ioctx.read("foo", bl3, bsize, bsize*3));
+
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_remove(my_snaps.back()));
+  my_snaps.pop_back();
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_remove(my_snaps.back()));
+  my_snaps.pop_back();
+  readioctx.close();
+
+  delete[] buf;
+  delete[] buf2;
+}
+
+TEST_F(LibRadosSnapshotsSelfManagedECPP, Bug11677) {
+  std::vector<uint64_t> my_snaps;
+  my_snaps.push_back(-2);
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps.back()));
+  ::std::reverse(my_snaps.begin(), my_snaps.end());
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
+  ::std::reverse(my_snaps.begin(), my_snaps.end());
+
+  int bsize = alignment;
+  char *buf = (char *)new char[bsize];
+  memset(buf, 0xcc, bsize);
+  bufferlist bl1;
+  bl1.append(buf, bsize);
+  ASSERT_EQ(0, ioctx.write("foo", bl1, bsize, 0));
+
+  my_snaps.push_back(-2);
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps.back()));
+  ::std::reverse(my_snaps.begin(), my_snaps.end());
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0], my_snaps));
+  ::std::reverse(my_snaps.begin(), my_snaps.end());
+
+  std::unique_ptr<librados::ObjectWriteOperation> op(new librados::ObjectWriteOperation());
+  op->assert_exists();
+  op->remove();
+  ASSERT_EQ(0, ioctx.operate("foo", op.get()));
+
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_remove(my_snaps.back()));
+  my_snaps.pop_back();
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_remove(my_snaps.back()));
+  my_snaps.pop_back();
+  ioctx.snap_set_read(LIBRADOS_SNAP_HEAD);
+  delete[] buf;
+}
index 9641bb8138d2676d6705bca795825639cc37f138..fd667742738734c0cd6462bd91a900ade01fec19 100644 (file)
@@ -1,5 +1,4 @@
 #include "include/rados/librados.h"
-#include "include/rados/librados.hpp"
 #include "test/librados/test.h"
 #include "test/librados/TestCase.h"
 
@@ -9,12 +8,8 @@
 #include <errno.h>
 #include "gtest/gtest.h"
 
-using namespace librados;
-
 typedef RadosTest LibRadosStat;
-typedef RadosTestPP LibRadosStatPP;
 typedef RadosTestEC LibRadosStatEC;
-typedef RadosTestECPP LibRadosStatECPP;
 
 TEST_F(LibRadosStat, Stat) {
   char buf[128];
@@ -27,49 +22,6 @@ TEST_F(LibRadosStat, Stat) {
   ASSERT_EQ(-ENOENT, rados_stat(ioctx, "nonexistent", &size, &mtime));
 }
 
-TEST_F(LibRadosStatPP, StatPP) {
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl;
-  bl.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
-  uint64_t size;
-  time_t mtime;
-  ASSERT_EQ(0, ioctx.stat("foo", &size, &mtime));
-  ASSERT_EQ(sizeof(buf), size);
-  ASSERT_EQ(-ENOENT, ioctx.stat("nonexistent", &size, &mtime));
-}
-
-TEST_F(LibRadosStatPP, Stat2Mtime2PP) {
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl;
-  bl.append(buf, sizeof(buf));
-  librados::ObjectWriteOperation op;
-  struct timespec ts;
-  ts.tv_sec = 1457129052;
-  ts.tv_nsec = 123456789;
-  op.mtime2(&ts);
-  op.write(0, bl);
-  ASSERT_EQ(0, ioctx.operate("foo", &op));
-
-  /* XXX time comparison asserts could spuriously fail */
-
-  uint64_t size;
-  time_t mtime;
-  ASSERT_EQ(0, ioctx.stat("foo", &size, &mtime));
-  ASSERT_EQ(sizeof(buf), size);
-  ASSERT_EQ(mtime, ts.tv_sec);
-
-  struct timespec ts2;
-  ASSERT_EQ(0, ioctx.stat2("foo", &size, &ts2));
-  ASSERT_EQ(sizeof(buf), size);
-  ASSERT_EQ(ts2.tv_sec, ts.tv_sec);
-  ASSERT_EQ(ts2.tv_nsec, ts.tv_nsec);
-
-  ASSERT_EQ(-ENOENT, ioctx.stat2("nonexistent", &size, &ts2));
-}
-
 TEST_F(LibRadosStat, StatNS) {
   char buf[128];
   memset(buf, 0xcc, sizeof(buf));
@@ -96,46 +48,11 @@ TEST_F(LibRadosStat, StatNS) {
   ASSERT_EQ(-ENOENT, rados_stat(ioctx, "foo2", &size, &mtime));
 }
 
-TEST_F(LibRadosStatPP, StatPPNS) {
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl;
-  bl.append(buf, sizeof(buf));
-  ioctx.set_namespace("");
-  ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
-  ASSERT_EQ(0, ioctx.write("foo2", bl, sizeof(buf), 0));
-
-  char buf2[64];
-  memset(buf2, 0xbb, sizeof(buf2));
-  bufferlist bl2;
-  bl2.append(buf2, sizeof(buf2));
-  ioctx.set_namespace("nspace");
-  ASSERT_EQ(0, ioctx.write("foo", bl2, sizeof(buf2), 0));
-
-  uint64_t size;
-  time_t mtime;
-  ioctx.set_namespace("");
-  ASSERT_EQ(0, ioctx.stat("foo", &size, &mtime));
-  ASSERT_EQ(sizeof(buf), size);
-  ASSERT_EQ(-ENOENT, ioctx.stat("nonexistent", &size, &mtime));
-
-  ioctx.set_namespace("nspace");
-  ASSERT_EQ(0, ioctx.stat("foo", &size, &mtime));
-  ASSERT_EQ(sizeof(buf2), size);
-  ASSERT_EQ(-ENOENT, ioctx.stat("nonexistent", &size, &mtime));
-  ASSERT_EQ(-ENOENT, ioctx.stat("foo2", &size, &mtime));
-}
-
 TEST_F(LibRadosStat, ClusterStat) {
   struct rados_cluster_stat_t result;
   ASSERT_EQ(0, rados_cluster_stat(cluster, &result));
 }
 
-TEST_F(LibRadosStatPP, ClusterStatPP) {
-  cluster_stat_t cstat;
-  ASSERT_EQ(0, cluster.cluster_stat(cstat));
-}
-
 TEST_F(LibRadosStat, PoolStat) {
   char buf[128];
   char actual_pool_name[80];
@@ -149,19 +66,6 @@ TEST_F(LibRadosStat, PoolStat) {
   ASSERT_EQ(0, rados_ioctx_pool_stat(ioctx, &stats));
 }
 
-TEST_F(LibRadosStatPP, PoolStatPP) {
-  std::string n = ioctx.get_pool_name();
-  ASSERT_EQ(n, pool_name);
-  char buf[128];
-  memset(buf, 0xff, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
-  std::list<std::string> v;
-  std::map<std::string,stats_map> stats;
-  ASSERT_EQ(0, cluster.get_pool_stats(v, stats));
-}
-
 TEST_F(LibRadosStatEC, Stat) {
   char buf[128];
   memset(buf, 0xcc, sizeof(buf));
@@ -173,19 +77,6 @@ TEST_F(LibRadosStatEC, Stat) {
   ASSERT_EQ(-ENOENT, rados_stat(ioctx, "nonexistent", &size, &mtime));
 }
 
-TEST_F(LibRadosStatECPP, StatPP) {
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl;
-  bl.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
-  uint64_t size;
-  time_t mtime;
-  ASSERT_EQ(0, ioctx.stat("foo", &size, &mtime));
-  ASSERT_EQ(sizeof(buf), size);
-  ASSERT_EQ(-ENOENT, ioctx.stat("nonexistent", &size, &mtime));
-}
-
 TEST_F(LibRadosStatEC, StatNS) {
   char buf[128];
   memset(buf, 0xcc, sizeof(buf));
@@ -212,46 +103,11 @@ TEST_F(LibRadosStatEC, StatNS) {
   ASSERT_EQ(-ENOENT, rados_stat(ioctx, "foo2", &size, &mtime));
 }
 
-TEST_F(LibRadosStatECPP, StatPPNS) {
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl;
-  bl.append(buf, sizeof(buf));
-  ioctx.set_namespace("");
-  ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
-  ASSERT_EQ(0, ioctx.write("foo2", bl, sizeof(buf), 0));
-
-  char buf2[64];
-  memset(buf2, 0xbb, sizeof(buf2));
-  bufferlist bl2;
-  bl2.append(buf2, sizeof(buf2));
-  ioctx.set_namespace("nspace");
-  ASSERT_EQ(0, ioctx.write("foo", bl2, sizeof(buf2), 0));
-
-  uint64_t size;
-  time_t mtime;
-  ioctx.set_namespace("");
-  ASSERT_EQ(0, ioctx.stat("foo", &size, &mtime));
-  ASSERT_EQ(sizeof(buf), size);
-  ASSERT_EQ(-ENOENT, ioctx.stat("nonexistent", &size, &mtime));
-
-  ioctx.set_namespace("nspace");
-  ASSERT_EQ(0, ioctx.stat("foo", &size, &mtime));
-  ASSERT_EQ(sizeof(buf2), size);
-  ASSERT_EQ(-ENOENT, ioctx.stat("nonexistent", &size, &mtime));
-  ASSERT_EQ(-ENOENT, ioctx.stat("foo2", &size, &mtime));
-}
-
 TEST_F(LibRadosStatEC, ClusterStat) {
   struct rados_cluster_stat_t result;
   ASSERT_EQ(0, rados_cluster_stat(cluster, &result));
 }
 
-TEST_F(LibRadosStatECPP, ClusterStatPP) {
-  cluster_stat_t cstat;
-  ASSERT_EQ(0, cluster.cluster_stat(cstat));
-}
-
 TEST_F(LibRadosStatEC, PoolStat) {
   char buf[128];
   char actual_pool_name[80];
@@ -264,16 +120,3 @@ TEST_F(LibRadosStatEC, PoolStat) {
   memset(&stats, 0, sizeof(stats));
   ASSERT_EQ(0, rados_ioctx_pool_stat(ioctx, &stats));
 }
-
-TEST_F(LibRadosStatECPP, PoolStatPP) {
-  std::string n = ioctx.get_pool_name();
-  ASSERT_EQ(n, pool_name);
-  char buf[128];
-  memset(buf, 0xff, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
-  std::list<std::string> v;
-  std::map<std::string,stats_map> stats;
-  ASSERT_EQ(0, cluster.get_pool_stats(v, stats));
-}
diff --git a/src/test/librados/stat_cxx.cc b/src/test/librados/stat_cxx.cc
new file mode 100644 (file)
index 0000000..d0d0116
--- /dev/null
@@ -0,0 +1,163 @@
+#include "gtest/gtest.h"
+
+#include "include/rados/librados.hpp"
+
+#include "test/librados/test_cxx.h"
+#include "test/librados/testcase_cxx.h"
+
+using namespace librados;
+
+typedef RadosTestPP LibRadosStatPP;
+typedef RadosTestECPP LibRadosStatECPP;
+
+TEST_F(LibRadosStatPP, StatPP) {
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl;
+  bl.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
+  uint64_t size;
+  time_t mtime;
+  ASSERT_EQ(0, ioctx.stat("foo", &size, &mtime));
+  ASSERT_EQ(sizeof(buf), size);
+  ASSERT_EQ(-ENOENT, ioctx.stat("nonexistent", &size, &mtime));
+}
+
+TEST_F(LibRadosStatPP, Stat2Mtime2PP) {
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl;
+  bl.append(buf, sizeof(buf));
+  librados::ObjectWriteOperation op;
+  struct timespec ts;
+  ts.tv_sec = 1457129052;
+  ts.tv_nsec = 123456789;
+  op.mtime2(&ts);
+  op.write(0, bl);
+  ASSERT_EQ(0, ioctx.operate("foo", &op));
+
+  /* XXX time comparison asserts could spuriously fail */
+
+  uint64_t size;
+  time_t mtime;
+  ASSERT_EQ(0, ioctx.stat("foo", &size, &mtime));
+  ASSERT_EQ(sizeof(buf), size);
+  ASSERT_EQ(mtime, ts.tv_sec);
+
+  struct timespec ts2;
+  ASSERT_EQ(0, ioctx.stat2("foo", &size, &ts2));
+  ASSERT_EQ(sizeof(buf), size);
+  ASSERT_EQ(ts2.tv_sec, ts.tv_sec);
+  ASSERT_EQ(ts2.tv_nsec, ts.tv_nsec);
+
+  ASSERT_EQ(-ENOENT, ioctx.stat2("nonexistent", &size, &ts2));
+}
+
+TEST_F(LibRadosStatPP, ClusterStatPP) {
+  cluster_stat_t cstat;
+  ASSERT_EQ(0, cluster.cluster_stat(cstat));
+}
+
+TEST_F(LibRadosStatPP, PoolStatPP) {
+  std::string n = ioctx.get_pool_name();
+  ASSERT_EQ(n, pool_name);
+  char buf[128];
+  memset(buf, 0xff, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+  std::list<std::string> v;
+  std::map<std::string,stats_map> stats;
+  ASSERT_EQ(0, cluster.get_pool_stats(v, stats));
+}
+
+TEST_F(LibRadosStatECPP, StatPP) {
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl;
+  bl.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
+  uint64_t size;
+  time_t mtime;
+  ASSERT_EQ(0, ioctx.stat("foo", &size, &mtime));
+  ASSERT_EQ(sizeof(buf), size);
+  ASSERT_EQ(-ENOENT, ioctx.stat("nonexistent", &size, &mtime));
+}
+
+TEST_F(LibRadosStatECPP, ClusterStatPP) {
+  cluster_stat_t cstat;
+  ASSERT_EQ(0, cluster.cluster_stat(cstat));
+}
+
+TEST_F(LibRadosStatECPP, PoolStatPP) {
+  std::string n = ioctx.get_pool_name();
+  ASSERT_EQ(n, pool_name);
+  char buf[128];
+  memset(buf, 0xff, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+  std::list<std::string> v;
+  std::map<std::string,stats_map> stats;
+  ASSERT_EQ(0, cluster.get_pool_stats(v, stats));
+}
+
+TEST_F(LibRadosStatPP, StatPPNS) {
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl;
+  bl.append(buf, sizeof(buf));
+  ioctx.set_namespace("");
+  ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
+  ASSERT_EQ(0, ioctx.write("foo2", bl, sizeof(buf), 0));
+
+  char buf2[64];
+  memset(buf2, 0xbb, sizeof(buf2));
+  bufferlist bl2;
+  bl2.append(buf2, sizeof(buf2));
+  ioctx.set_namespace("nspace");
+  ASSERT_EQ(0, ioctx.write("foo", bl2, sizeof(buf2), 0));
+
+  uint64_t size;
+  time_t mtime;
+  ioctx.set_namespace("");
+  ASSERT_EQ(0, ioctx.stat("foo", &size, &mtime));
+  ASSERT_EQ(sizeof(buf), size);
+  ASSERT_EQ(-ENOENT, ioctx.stat("nonexistent", &size, &mtime));
+
+  ioctx.set_namespace("nspace");
+  ASSERT_EQ(0, ioctx.stat("foo", &size, &mtime));
+  ASSERT_EQ(sizeof(buf2), size);
+  ASSERT_EQ(-ENOENT, ioctx.stat("nonexistent", &size, &mtime));
+  ASSERT_EQ(-ENOENT, ioctx.stat("foo2", &size, &mtime));
+}
+
+TEST_F(LibRadosStatECPP, StatPPNS) {
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl;
+  bl.append(buf, sizeof(buf));
+  ioctx.set_namespace("");
+  ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
+  ASSERT_EQ(0, ioctx.write("foo2", bl, sizeof(buf), 0));
+
+  char buf2[64];
+  memset(buf2, 0xbb, sizeof(buf2));
+  bufferlist bl2;
+  bl2.append(buf2, sizeof(buf2));
+  ioctx.set_namespace("nspace");
+  ASSERT_EQ(0, ioctx.write("foo", bl2, sizeof(buf2), 0));
+
+  uint64_t size;
+  time_t mtime;
+  ioctx.set_namespace("");
+  ASSERT_EQ(0, ioctx.stat("foo", &size, &mtime));
+  ASSERT_EQ(sizeof(buf), size);
+  ASSERT_EQ(-ENOENT, ioctx.stat("nonexistent", &size, &mtime));
+
+  ioctx.set_namespace("nspace");
+  ASSERT_EQ(0, ioctx.stat("foo", &size, &mtime));
+  ASSERT_EQ(sizeof(buf2), size);
+  ASSERT_EQ(-ENOENT, ioctx.stat("nonexistent", &size, &mtime));
+  ASSERT_EQ(-ENOENT, ioctx.stat("foo2", &size, &mtime));
+}
index 04fd0452e66bdbe59a6319111a3da869266d6e70..fdfbc54199cca6f1e1ea926080b72fde43d892fc 100644 (file)
 #include <iostream>
 #include "gtest/gtest.h"
 
-using namespace librados;
-
-std::string get_temp_pool_name(const std::string &prefix)
-{
-  char hostname[80];
-  char out[160];
-  memset(hostname, 0, sizeof(hostname));
-  memset(out, 0, sizeof(out));
-  gethostname(hostname, sizeof(hostname)-1);
-  static int num = 1;
-  snprintf(out, sizeof(out), "%s-%d-%d", hostname, getpid(), num);
-  num++;
-  return prefix + out;
-}
-
-
 std::string create_one_pool(
     const std::string &pool_name, rados_t *cluster, uint32_t pg_num)
 {
@@ -149,109 +133,6 @@ std::string create_one_ec_pool(const std::string &pool_name, rados_t *cluster)
   return "";
 }
 
-std::string create_one_pool_pp(const std::string &pool_name, Rados &cluster)
-{
-    return create_one_pool_pp(pool_name, cluster, {});
-}
-std::string create_one_pool_pp(const std::string &pool_name, Rados &cluster,
-                               const std::map<std::string, std::string> &config)
-{
-  std::string err = connect_cluster_pp(cluster, config);
-  if (err.length())
-    return err;
-  int ret = cluster.pool_create(pool_name.c_str());
-  if (ret) {
-    cluster.shutdown();
-    std::ostringstream oss;
-    oss << "cluster.pool_create(" << pool_name << ") failed with error " << ret;
-    return oss.str();
-  }
-
-  IoCtx ioctx;
-  ret = cluster.ioctx_create(pool_name.c_str(), ioctx);
-  if (ret < 0) {
-    cluster.shutdown();
-    std::ostringstream oss;
-    oss << "cluster.ioctx_create(" << pool_name << ") failed with error "
-        << ret;
-    return oss.str();
-  }
-  ioctx.application_enable("rados", true);
-  return "";
-}
-
-int destroy_ruleset_pp(Rados &cluster,
-                       const std::string &ruleset,
-                       std::ostream &oss)
-{
-  bufferlist inbl;
-  int ret = cluster.mon_command("{\"prefix\": \"osd crush rule rm\", \"name\":\"" +
-                                ruleset + "\"}", inbl, NULL, NULL);
-  if (ret)
-    oss << "mon_command: osd crush rule rm " + ruleset + " failed with error " << ret << std::endl;
-  return ret;
-}
-
-int destroy_ec_profile_pp(Rados &cluster, const std::string& pool_name,
-                         std::ostream &oss)
-{
-  bufferlist inbl;
-  int ret = cluster.mon_command("{\"prefix\": \"osd erasure-code-profile rm\", \"name\": \"testprofile-" + pool_name + "\"}",
-                                inbl, NULL, NULL);
-  if (ret)
-    oss << "mon_command: osd erasure-code-profile rm testprofile-" << pool_name << " failed with error " << ret << std::endl;
-  return ret;
-}
-
-int destroy_ec_profile_and_ruleset_pp(Rados &cluster,
-                                      const std::string &ruleset,
-                                      std::ostream &oss)
-{
-  int ret;
-  ret = destroy_ec_profile_pp(cluster, ruleset, oss);
-  if (ret)
-    return ret;
-  return destroy_ruleset_pp(cluster, ruleset, oss);
-}
-
-std::string create_one_ec_pool_pp(const std::string &pool_name, Rados &cluster)
-{
-  std::string err = connect_cluster_pp(cluster);
-  if (err.length())
-    return err;
-
-  std::ostringstream oss;
-  int ret = destroy_ec_profile_and_ruleset_pp(cluster, pool_name, oss);
-  if (ret) {
-    cluster.shutdown();
-    return oss.str();
-  }
-
-  bufferlist inbl;
-  ret = cluster.mon_command(
-    "{\"prefix\": \"osd erasure-code-profile set\", \"name\": \"testprofile-" + pool_name + "\", \"profile\": [ \"k=2\", \"m=1\", \"crush-failure-domain=osd\"]}",
-    inbl, NULL, NULL);
-  if (ret) {
-    cluster.shutdown();
-    oss << "mon_command erasure-code-profile set name:testprofile-" << pool_name << " failed with error " << ret;
-    return oss.str();
-  }
-    
-  ret = cluster.mon_command(
-    "{\"prefix\": \"osd pool create\", \"pool\": \"" + pool_name + "\", \"pool_type\":\"erasure\", \"pg_num\":8, \"pgp_num\":8, \"erasure_code_profile\":\"testprofile-" + pool_name + "\"}",
-    inbl, NULL, NULL);
-  if (ret) {
-    bufferlist inbl;
-    destroy_ec_profile_pp(cluster, pool_name, oss);
-    cluster.shutdown();
-    oss << "mon_command osd pool create pool:" << pool_name << " pool_type:erasure failed with error " << ret;
-    return oss.str();
-  }
-
-  cluster.wait_for_latest_osdmap();
-  return "";
-}
-
 std::string connect_cluster(rados_t *cluster)
 {
   char *id = getenv("CEPH_CLIENT_ID");
@@ -282,53 +163,6 @@ std::string connect_cluster(rados_t *cluster)
   return "";
 }
 
-std::string connect_cluster_pp(librados::Rados &cluster)
-{
-  return connect_cluster_pp(cluster, {});
-}
-
-std::string connect_cluster_pp(librados::Rados &cluster,
-                               const std::map<std::string, std::string> &config)
-{
-  char *id = getenv("CEPH_CLIENT_ID");
-  if (id) std::cerr << "Client id is: " << id << std::endl;
-
-  int ret;
-  ret = cluster.init(id);
-  if (ret) {
-    std::ostringstream oss;
-    oss << "cluster.init failed with error " << ret;
-    return oss.str();
-  }
-  ret = cluster.conf_read_file(NULL);
-  if (ret) {
-    cluster.shutdown();
-    std::ostringstream oss;
-    oss << "cluster.conf_read_file failed with error " << ret;
-    return oss.str();
-  }
-  cluster.conf_parse_env(NULL);
-
-  for (auto &setting : config) {
-    ret = cluster.conf_set(setting.first.c_str(), setting.second.c_str());
-    if (ret) {
-      std::ostringstream oss;
-      oss << "failed to set config value " << setting.first << " to '"
-          << setting.second << "': " << strerror(-ret);
-      return oss.str();
-    }
-  }
-
-  ret = cluster.connect();
-  if (ret) {
-    cluster.shutdown();
-    std::ostringstream oss;
-    oss << "cluster.connect failed with error " << ret;
-    return oss.str();
-  }
-  return "";
-}
-
 int destroy_one_pool(const std::string &pool_name, rados_t *cluster)
 {
   int ret = rados_pool_delete(*cluster, pool_name.c_str());
@@ -362,61 +196,3 @@ int destroy_one_ec_pool(const std::string &pool_name, rados_t *cluster)
   rados_shutdown(*cluster);
   return ret;
 }
-
-int destroy_one_pool_pp(const std::string &pool_name, Rados &cluster)
-{
-  int ret = cluster.pool_delete(pool_name.c_str());
-  if (ret) {
-    cluster.shutdown();
-    return ret;
-  }
-  cluster.shutdown();
-  return 0;
-}
-
-int destroy_one_ec_pool_pp(const std::string &pool_name, Rados &cluster)
-{
-  int ret = cluster.pool_delete(pool_name.c_str());
-  if (ret) {
-    cluster.shutdown();
-    return ret;
-  }
-
-  CephContext *cct = static_cast<CephContext*>(cluster.cct());
-  if (!cct->_conf->mon_fake_pool_delete) { // hope this is in [global]
-    std::ostringstream oss;
-    ret = destroy_ec_profile_and_ruleset_pp(cluster, pool_name, oss);
-    if (ret) {
-      cluster.shutdown();
-      return ret;
-    }
-  }
-
-  cluster.wait_for_latest_osdmap();
-  cluster.shutdown();
-  return ret;
-}
-
-void assert_eq_sparse(bufferlist& expected,
-                      const std::map<uint64_t, uint64_t>& extents,
-                      bufferlist& actual) {
-  auto i = expected.begin();
-  auto p = actual.begin();
-  uint64_t pos = 0;
-  for (auto extent : extents) {
-    const uint64_t start = extent.first;
-    const uint64_t end = start + extent.second;
-    for (; pos < end; ++i, ++pos) {
-      ASSERT_FALSE(i.end());
-      if (pos < start) {
-        // check the hole
-        ASSERT_EQ('\0', *i);
-      } else {
-        // then the extent
-        ASSERT_EQ(*i, *p);
-        ++p;
-      }
-    }
-  }
-  ASSERT_EQ(expected.length(), pos);
-}
index 8b505957fd08cfff5821069c3057038a58a4912f..b3e0115fb4660577e2c6790a1b9de5c862c827d0 100644 (file)
 #define CEPH_TEST_RADOS_API_TEST_H
 
 #include "include/rados/librados.h"
-#include "include/rados/librados.hpp"
+#include "test/librados/test_shared.h"
 
 #include <map>
 #include <string>
 #include <unistd.h>
 
-std::string get_temp_pool_name(const std::string &prefix = "test-rados-api-");
-
 std::string create_one_pool(const std::string &pool_name, rados_t *cluster,
     uint32_t pg_num=0);
 std::string create_one_ec_pool(const std::string &pool_name, rados_t *cluster);
-std::string create_one_pool_pp(const std::string &pool_name,
-                           librados::Rados &cluster);
-std::string create_one_pool_pp(const std::string &pool_name,
-                              librados::Rados &cluster,
-                              const std::map<std::string, std::string> &config);
-std::string create_one_ec_pool_pp(const std::string &pool_name,
-                           librados::Rados &cluster);
 std::string connect_cluster(rados_t *cluster);
-std::string connect_cluster_pp(librados::Rados &cluster);
-std::string connect_cluster_pp(librados::Rados &cluster,
-                              const std::map<std::string, std::string> &config);
 int destroy_one_pool(const std::string &pool_name, rados_t *cluster);
 int destroy_one_ec_pool(const std::string &pool_name, rados_t *cluster);
-int destroy_one_pool_pp(const std::string &pool_name, librados::Rados &cluster);
-int destroy_one_ec_pool_pp(const std::string &pool_name, librados::Rados &cluster);
-void assert_eq_sparse(bufferlist& expected,
-                      const std::map<uint64_t, uint64_t>& extents,
-                      bufferlist& actual);
-
-class TestAlarm
-{
-public:
-  TestAlarm() {
-    alarm(1200);
-  }
-  ~TestAlarm() {
-    alarm(0);
-  }
-};
 
 #endif
diff --git a/src/test/librados/test_cxx.cc b/src/test/librados/test_cxx.cc
new file mode 100644 (file)
index 0000000..9349aec
--- /dev/null
@@ -0,0 +1,203 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*
+// vim: ts=8 sw=2 smarttab
+
+#include "test_cxx.h"
+
+#include "include/stringify.h"
+#include "common/ceph_context.h"
+#include "common/config.h"
+
+#include <errno.h>
+#include <sstream>
+#include <stdlib.h>
+#include <string>
+#include <time.h>
+#include <unistd.h>
+#include <iostream>
+#include "gtest/gtest.h"
+
+using namespace librados;
+
+std::string create_one_pool_pp(const std::string &pool_name, Rados &cluster)
+{
+    return create_one_pool_pp(pool_name, cluster, {});
+}
+std::string create_one_pool_pp(const std::string &pool_name, Rados &cluster,
+                               const std::map<std::string, std::string> &config)
+{
+  std::string err = connect_cluster_pp(cluster, config);
+  if (err.length())
+    return err;
+  int ret = cluster.pool_create(pool_name.c_str());
+  if (ret) {
+    cluster.shutdown();
+    std::ostringstream oss;
+    oss << "cluster.pool_create(" << pool_name << ") failed with error " << ret;
+    return oss.str();
+  }
+
+  IoCtx ioctx;
+  ret = cluster.ioctx_create(pool_name.c_str(), ioctx);
+  if (ret < 0) {
+    cluster.shutdown();
+    std::ostringstream oss;
+    oss << "cluster.ioctx_create(" << pool_name << ") failed with error "
+        << ret;
+    return oss.str();
+  }
+  ioctx.application_enable("rados", true);
+  return "";
+}
+
+int destroy_ruleset_pp(Rados &cluster,
+                       const std::string &ruleset,
+                       std::ostream &oss)
+{
+  bufferlist inbl;
+  int ret = cluster.mon_command("{\"prefix\": \"osd crush rule rm\", \"name\":\"" +
+                                ruleset + "\"}", inbl, NULL, NULL);
+  if (ret)
+    oss << "mon_command: osd crush rule rm " + ruleset + " failed with error " << ret << std::endl;
+  return ret;
+}
+
+int destroy_ec_profile_pp(Rados &cluster, const std::string& pool_name,
+                         std::ostream &oss)
+{
+  bufferlist inbl;
+  int ret = cluster.mon_command("{\"prefix\": \"osd erasure-code-profile rm\", \"name\": \"testprofile-" + pool_name + "\"}",
+                                inbl, NULL, NULL);
+  if (ret)
+    oss << "mon_command: osd erasure-code-profile rm testprofile-" << pool_name << " failed with error " << ret << std::endl;
+  return ret;
+}
+
+int destroy_ec_profile_and_ruleset_pp(Rados &cluster,
+                                      const std::string &ruleset,
+                                      std::ostream &oss)
+{
+  int ret;
+  ret = destroy_ec_profile_pp(cluster, ruleset, oss);
+  if (ret)
+    return ret;
+  return destroy_ruleset_pp(cluster, ruleset, oss);
+}
+
+std::string create_one_ec_pool_pp(const std::string &pool_name, Rados &cluster)
+{
+  std::string err = connect_cluster_pp(cluster);
+  if (err.length())
+    return err;
+
+  std::ostringstream oss;
+  int ret = destroy_ec_profile_and_ruleset_pp(cluster, pool_name, oss);
+  if (ret) {
+    cluster.shutdown();
+    return oss.str();
+  }
+
+  bufferlist inbl;
+  ret = cluster.mon_command(
+    "{\"prefix\": \"osd erasure-code-profile set\", \"name\": \"testprofile-" + pool_name + "\", \"profile\": [ \"k=2\", \"m=1\", \"crush-failure-domain=osd\"]}",
+    inbl, NULL, NULL);
+  if (ret) {
+    cluster.shutdown();
+    oss << "mon_command erasure-code-profile set name:testprofile-" << pool_name << " failed with error " << ret;
+    return oss.str();
+  }
+    
+  ret = cluster.mon_command(
+    "{\"prefix\": \"osd pool create\", \"pool\": \"" + pool_name + "\", \"pool_type\":\"erasure\", \"pg_num\":8, \"pgp_num\":8, \"erasure_code_profile\":\"testprofile-" + pool_name + "\"}",
+    inbl, NULL, NULL);
+  if (ret) {
+    bufferlist inbl;
+    destroy_ec_profile_pp(cluster, pool_name, oss);
+    cluster.shutdown();
+    oss << "mon_command osd pool create pool:" << pool_name << " pool_type:erasure failed with error " << ret;
+    return oss.str();
+  }
+
+  cluster.wait_for_latest_osdmap();
+  return "";
+}
+
+std::string connect_cluster_pp(librados::Rados &cluster)
+{
+  return connect_cluster_pp(cluster, {});
+}
+
+std::string connect_cluster_pp(librados::Rados &cluster,
+                               const std::map<std::string, std::string> &config)
+{
+  char *id = getenv("CEPH_CLIENT_ID");
+  if (id) std::cerr << "Client id is: " << id << std::endl;
+
+  int ret;
+  ret = cluster.init(id);
+  if (ret) {
+    std::ostringstream oss;
+    oss << "cluster.init failed with error " << ret;
+    return oss.str();
+  }
+  ret = cluster.conf_read_file(NULL);
+  if (ret) {
+    cluster.shutdown();
+    std::ostringstream oss;
+    oss << "cluster.conf_read_file failed with error " << ret;
+    return oss.str();
+  }
+  cluster.conf_parse_env(NULL);
+
+  for (auto &setting : config) {
+    ret = cluster.conf_set(setting.first.c_str(), setting.second.c_str());
+    if (ret) {
+      std::ostringstream oss;
+      oss << "failed to set config value " << setting.first << " to '"
+          << setting.second << "': " << strerror(-ret);
+      return oss.str();
+    }
+  }
+
+  ret = cluster.connect();
+  if (ret) {
+    cluster.shutdown();
+    std::ostringstream oss;
+    oss << "cluster.connect failed with error " << ret;
+    return oss.str();
+  }
+  return "";
+}
+
+int destroy_one_pool_pp(const std::string &pool_name, Rados &cluster)
+{
+  int ret = cluster.pool_delete(pool_name.c_str());
+  if (ret) {
+    cluster.shutdown();
+    return ret;
+  }
+  cluster.shutdown();
+  return 0;
+}
+
+int destroy_one_ec_pool_pp(const std::string &pool_name, Rados &cluster)
+{
+  int ret = cluster.pool_delete(pool_name.c_str());
+  if (ret) {
+    cluster.shutdown();
+    return ret;
+  }
+
+  CephContext *cct = static_cast<CephContext*>(cluster.cct());
+  if (!cct->_conf->mon_fake_pool_delete) { // hope this is in [global]
+    std::ostringstream oss;
+    ret = destroy_ec_profile_and_ruleset_pp(cluster, pool_name, oss);
+    if (ret) {
+      cluster.shutdown();
+      return ret;
+    }
+  }
+
+  cluster.wait_for_latest_osdmap();
+  cluster.shutdown();
+  return ret;
+}
diff --git a/src/test/librados/test_cxx.h b/src/test/librados/test_cxx.h
new file mode 100644 (file)
index 0000000..1d11d69
--- /dev/null
@@ -0,0 +1,19 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#pragma once
+
+#include "include/rados/librados.hpp"
+#include "test/librados/test_shared.h"
+
+std::string create_one_pool_pp(const std::string &pool_name,
+                           librados::Rados &cluster);
+std::string create_one_pool_pp(const std::string &pool_name,
+                              librados::Rados &cluster,
+                              const std::map<std::string, std::string> &config);
+std::string create_one_ec_pool_pp(const std::string &pool_name,
+                           librados::Rados &cluster);
+std::string connect_cluster_pp(librados::Rados &cluster);
+std::string connect_cluster_pp(librados::Rados &cluster,
+                              const std::map<std::string, std::string> &config);
+int destroy_one_pool_pp(const std::string &pool_name, librados::Rados &cluster);
+int destroy_one_ec_pool_pp(const std::string &pool_name, librados::Rados &cluster);
diff --git a/src/test/librados/test_shared.cc b/src/test/librados/test_shared.cc
new file mode 100644 (file)
index 0000000..8b50d11
--- /dev/null
@@ -0,0 +1,44 @@
+#include "test_shared.h"
+
+#include <cstring>
+#include "gtest/gtest.h"
+#include "include/buffer.h"
+
+using namespace ceph;
+
+std::string get_temp_pool_name(const std::string &prefix)
+{
+  char hostname[80];
+  char out[160];
+  memset(hostname, 0, sizeof(hostname));
+  memset(out, 0, sizeof(out));
+  gethostname(hostname, sizeof(hostname)-1);
+  static int num = 1;
+  snprintf(out, sizeof(out), "%s-%d-%d", hostname, getpid(), num);
+  num++;
+  return prefix + out;
+}
+
+void assert_eq_sparse(bufferlist& expected,
+                      const std::map<uint64_t, uint64_t>& extents,
+                      bufferlist& actual) {
+  auto i = expected.begin();
+  auto p = actual.begin();
+  uint64_t pos = 0;
+  for (auto extent : extents) {
+    const uint64_t start = extent.first;
+    const uint64_t end = start + extent.second;
+    for (; pos < end; ++i, ++pos) {
+      ASSERT_FALSE(i.end());
+      if (pos < start) {
+        // check the hole
+        ASSERT_EQ('\0', *i);
+      } else {
+        // then the extent
+        ASSERT_EQ(*i, *p);
+        ++p;
+      }
+    }
+  }
+  ASSERT_EQ(expected.length(), pos);
+}
diff --git a/src/test/librados/test_shared.h b/src/test/librados/test_shared.h
new file mode 100644 (file)
index 0000000..29e23b4
--- /dev/null
@@ -0,0 +1,49 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*
+// vim: ts=8 sw=2 smarttab
+
+#pragma once
+
+#include <unistd.h>
+#include <chrono>
+#include <map>
+#include <string>
+#include <thread>
+
+#include "include/buffer_fwd.h"
+
+// helpers shared by librados and librados-cxx tests
+std::string get_temp_pool_name(const std::string &prefix = "test-rados-api-");
+void assert_eq_sparse(ceph::bufferlist& expected,
+                      const std::map<uint64_t, uint64_t>& extents,
+                      ceph::bufferlist& actual);
+class TestAlarm
+{
+public:
+  TestAlarm() {
+    alarm(1200);
+  }
+  ~TestAlarm() {
+    alarm(0);
+  }
+};
+
+template<class Rep, class Period, typename Func, typename... Args,
+         typename Return = std::result_of_t<Func&&(Args&&...)>>
+Return wait_until(const std::chrono::duration<Rep, Period>& rel_time,
+                const std::chrono::duration<Rep, Period>& step,
+                const Return& expected,
+                Func&& func, Args&&... args)
+{
+  std::this_thread::sleep_for(rel_time - step);
+  for (auto& s : {step, step}) {
+    if (!s.count()) {
+      break;
+    }
+    auto ret = func(std::forward<Args>(args)...);
+    if (ret == expected) {
+      return ret;
+    }
+    std::this_thread::sleep_for(s);
+  }
+  return func(std::forward<Args>(args)...);
+}
diff --git a/src/test/librados/testcase_cxx.cc b/src/test/librados/testcase_cxx.cc
new file mode 100644 (file)
index 0000000..0a71fa3
--- /dev/null
@@ -0,0 +1,390 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "testcase_cxx.h"
+
+#include <errno.h>
+#include "test_cxx.h"
+#include "test_shared.h"
+#include "include/scope_guard.h"
+
+using namespace librados;
+
+namespace {
+
+void init_rand() {
+  static bool seeded = false;
+  if (!seeded) {
+    seeded = true;
+    int seed = getpid();
+    std::cout << "seed " << seed << std::endl;
+    srand(seed);
+  }
+}
+
+} // anonymous namespace
+
+std::string RadosTestPPNS::pool_name;
+Rados RadosTestPPNS::s_cluster;
+
+void RadosTestPPNS::SetUpTestCase()
+{
+  pool_name = get_temp_pool_name();
+  ASSERT_EQ("", create_one_pool_pp(pool_name, s_cluster));
+}
+
+void RadosTestPPNS::TearDownTestCase()
+{
+  ASSERT_EQ(0, destroy_one_pool_pp(pool_name, s_cluster));
+}
+
+void RadosTestPPNS::SetUp()
+{
+  ASSERT_EQ(0, cluster.ioctx_create(pool_name.c_str(), ioctx));
+  bool requires;
+  ASSERT_EQ(0, ioctx.pool_requires_alignment2(&requires));
+  ASSERT_FALSE(requires);
+}
+
+void RadosTestPPNS::TearDown()
+{
+  if (cleanup)
+    cleanup_all_objects(ioctx);
+  ioctx.close();
+}
+
+void RadosTestPPNS::cleanup_all_objects(librados::IoCtx ioctx)
+{
+  // remove all objects to avoid polluting other tests
+  ioctx.snap_set_read(librados::SNAP_HEAD);
+  ioctx.set_namespace(all_nspaces);
+  for (NObjectIterator it = ioctx.nobjects_begin();
+       it != ioctx.nobjects_end(); ++it) {
+    ioctx.locator_set_key(it->get_locator());
+    ioctx.set_namespace(it->get_nspace());
+    ASSERT_EQ(0, ioctx.remove(it->get_oid()));
+  }
+}
+
+std::string RadosTestParamPPNS::pool_name;
+std::string RadosTestParamPPNS::cache_pool_name;
+Rados RadosTestParamPPNS::s_cluster;
+
+void RadosTestParamPPNS::SetUpTestCase()
+{
+  pool_name = get_temp_pool_name();
+  ASSERT_EQ("", create_one_pool_pp(pool_name, s_cluster));
+}
+
+void RadosTestParamPPNS::TearDownTestCase()
+{
+  if (cache_pool_name.length()) {
+    // tear down tiers
+    bufferlist inbl;
+    ASSERT_EQ(0, s_cluster.mon_command(
+      "{\"prefix\": \"osd tier remove-overlay\", \"pool\": \"" + pool_name +
+      "\"}",
+      inbl, NULL, NULL));
+    ASSERT_EQ(0, s_cluster.mon_command(
+      "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
+      "\", \"tierpool\": \"" + cache_pool_name + "\"}",
+      inbl, NULL, NULL));
+    ASSERT_EQ(0, s_cluster.mon_command(
+      "{\"prefix\": \"osd pool delete\", \"pool\": \"" + cache_pool_name +
+      "\", \"pool2\": \"" + cache_pool_name + "\", \"sure\": \"--yes-i-really-really-mean-it\"}",
+      inbl, NULL, NULL));
+    cache_pool_name = "";
+  }
+  ASSERT_EQ(0, destroy_one_pool_pp(pool_name, s_cluster));
+}
+
+void RadosTestParamPPNS::SetUp()
+{
+  if (strcmp(GetParam(), "cache") == 0 && cache_pool_name.empty()) {
+    cache_pool_name = get_temp_pool_name();
+    bufferlist inbl;
+    ASSERT_EQ(0, cluster.mon_command(
+      "{\"prefix\": \"osd pool create\", \"pool\": \"" + cache_pool_name +
+      "\", \"pg_num\": 4}",
+      inbl, NULL, NULL));
+    ASSERT_EQ(0, cluster.mon_command(
+      "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+      "\", \"tierpool\": \"" + cache_pool_name +
+      "\", \"force_nonempty\": \"--force-nonempty\" }",
+      inbl, NULL, NULL));
+    ASSERT_EQ(0, cluster.mon_command(
+      "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+      "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+      inbl, NULL, NULL));
+    ASSERT_EQ(0, cluster.mon_command(
+      "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+      "\", \"mode\": \"writeback\"}",
+      inbl, NULL, NULL));
+    cluster.wait_for_latest_osdmap();
+  }
+
+  ASSERT_EQ(0, cluster.ioctx_create(pool_name.c_str(), ioctx));
+  bool requires;
+  ASSERT_EQ(0, ioctx.pool_requires_alignment2(&requires));
+  ASSERT_FALSE(requires);
+}
+
+void RadosTestParamPPNS::TearDown()
+{
+  if (cleanup)
+    cleanup_all_objects(ioctx);
+  ioctx.close();
+}
+
+void RadosTestParamPPNS::cleanup_all_objects(librados::IoCtx ioctx)
+{
+  // remove all objects to avoid polluting other tests
+  ioctx.snap_set_read(librados::SNAP_HEAD);
+  ioctx.set_namespace(all_nspaces);
+  for (NObjectIterator it = ioctx.nobjects_begin();
+       it != ioctx.nobjects_end(); ++it) {
+    ioctx.locator_set_key(it->get_locator());
+    ioctx.set_namespace(it->get_nspace());
+    ASSERT_EQ(0, ioctx.remove(it->get_oid()));
+  }
+}
+
+std::string RadosTestECPPNS::pool_name;
+Rados RadosTestECPPNS::s_cluster;
+
+void RadosTestECPPNS::SetUpTestCase()
+{
+  pool_name = get_temp_pool_name();
+  ASSERT_EQ("", create_one_ec_pool_pp(pool_name, s_cluster));
+}
+
+void RadosTestECPPNS::TearDownTestCase()
+{
+  ASSERT_EQ(0, destroy_one_ec_pool_pp(pool_name, s_cluster));
+}
+
+void RadosTestECPPNS::SetUp()
+{
+  ASSERT_EQ(0, cluster.ioctx_create(pool_name.c_str(), ioctx));
+  bool requires;
+  ASSERT_EQ(0, ioctx.pool_requires_alignment2(&requires));
+  ASSERT_TRUE(requires);
+  ASSERT_EQ(0, ioctx.pool_required_alignment2(&alignment));
+  ASSERT_NE(0U, alignment);
+}
+
+void RadosTestECPPNS::TearDown()
+{
+  if (cleanup)
+    cleanup_all_objects(ioctx);
+  ioctx.close();
+}
+
+std::string RadosTestPP::pool_name;
+Rados RadosTestPP::s_cluster;
+
+void RadosTestPP::SetUpTestCase()
+{
+  init_rand();
+
+  pool_name = get_temp_pool_name();
+  ASSERT_EQ("", create_one_pool_pp(pool_name, s_cluster));
+}
+
+void RadosTestPP::TearDownTestCase()
+{
+  ASSERT_EQ(0, destroy_one_pool_pp(pool_name, s_cluster));
+}
+
+void RadosTestPP::SetUp()
+{
+  ASSERT_EQ(0, cluster.ioctx_create(pool_name.c_str(), ioctx));
+  nspace = get_temp_pool_name();
+  ioctx.set_namespace(nspace);
+  bool requires;
+  ASSERT_EQ(0, ioctx.pool_requires_alignment2(&requires));
+  ASSERT_FALSE(requires);
+}
+
+void RadosTestPP::TearDown()
+{
+  if (cleanup) {
+    cleanup_default_namespace(ioctx);
+    cleanup_namespace(ioctx, nspace);
+  }
+  ioctx.close();
+}
+
+void RadosTestPP::cleanup_default_namespace(librados::IoCtx ioctx)
+{
+  // remove all objects from the default namespace to avoid polluting
+  // other tests
+  cleanup_namespace(ioctx, "");
+}
+
+void RadosTestPP::cleanup_namespace(librados::IoCtx ioctx, std::string ns)
+{
+  ioctx.snap_set_read(librados::SNAP_HEAD);
+  ioctx.set_namespace(ns);
+  int tries = 600;
+  while (--tries) {
+    int got_enoent = 0;
+    for (NObjectIterator it = ioctx.nobjects_begin();
+        it != ioctx.nobjects_end(); ++it) {
+      ioctx.locator_set_key(it->get_locator());
+      ObjectWriteOperation op;
+      op.remove();
+      librados::AioCompletion *completion = s_cluster.aio_create_completion();
+      auto sg = make_scope_guard([&] { completion->release(); });
+      ASSERT_EQ(0, ioctx.aio_operate(it->get_oid(), completion, &op,
+                                    librados::OPERATION_IGNORE_CACHE));
+      completion->wait_for_safe();
+      if (completion->get_return_value() == -ENOENT) {
+       ++got_enoent;
+       std::cout << " got ENOENT removing " << it->get_oid() << std::endl;
+      } else {
+       ASSERT_EQ(0, completion->get_return_value());
+      }
+    }
+    if (!got_enoent) {
+      break;
+    }
+    std::cout << " got ENOENT on " << got_enoent
+             << " objects, waiting a bit for snap"
+             << " trimming before retrying " << tries << " more times..."
+             << std::endl;
+    sleep(1);
+  }
+  if (tries == 0) {
+    std::cout << "failed to clean up" << std::endl;
+    ASSERT_TRUE(false);
+  }
+}
+
+std::string RadosTestParamPP::pool_name;
+std::string RadosTestParamPP::cache_pool_name;
+Rados RadosTestParamPP::s_cluster;
+
+void RadosTestParamPP::SetUpTestCase()
+{
+  pool_name = get_temp_pool_name();
+  ASSERT_EQ("", create_one_pool_pp(pool_name, s_cluster));
+}
+
+void RadosTestParamPP::TearDownTestCase()
+{
+  if (cache_pool_name.length()) {
+    // tear down tiers
+    bufferlist inbl;
+    ASSERT_EQ(0, s_cluster.mon_command(
+      "{\"prefix\": \"osd tier remove-overlay\", \"pool\": \"" + pool_name +
+      "\"}",
+      inbl, NULL, NULL));
+    ASSERT_EQ(0, s_cluster.mon_command(
+      "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
+      "\", \"tierpool\": \"" + cache_pool_name + "\"}",
+      inbl, NULL, NULL));
+    ASSERT_EQ(0, s_cluster.mon_command(
+      "{\"prefix\": \"osd pool delete\", \"pool\": \"" + cache_pool_name +
+      "\", \"pool2\": \"" + cache_pool_name + "\", \"sure\": \"--yes-i-really-really-mean-it\"}",
+      inbl, NULL, NULL));
+    cache_pool_name = "";
+  }
+  ASSERT_EQ(0, destroy_one_pool_pp(pool_name, s_cluster));
+}
+
+void RadosTestParamPP::SetUp()
+{
+  if (strcmp(GetParam(), "cache") == 0 && cache_pool_name.empty()) {
+    cache_pool_name = get_temp_pool_name();
+    bufferlist inbl;
+    ASSERT_EQ(0, cluster.mon_command(
+      "{\"prefix\": \"osd pool create\", \"pool\": \"" + cache_pool_name +
+      "\", \"pg_num\": 4}",
+      inbl, NULL, NULL));
+    ASSERT_EQ(0, cluster.mon_command(
+      "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+      "\", \"tierpool\": \"" + cache_pool_name +
+      "\", \"force_nonempty\": \"--force-nonempty\" }",
+      inbl, NULL, NULL));
+    ASSERT_EQ(0, cluster.mon_command(
+      "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+      "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+      inbl, NULL, NULL));
+    ASSERT_EQ(0, cluster.mon_command(
+      "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+      "\", \"mode\": \"writeback\"}",
+      inbl, NULL, NULL));
+    cluster.wait_for_latest_osdmap();
+  }
+
+  ASSERT_EQ(0, cluster.ioctx_create(pool_name.c_str(), ioctx));
+  nspace = get_temp_pool_name();
+  ioctx.set_namespace(nspace);
+  bool requires;
+  ASSERT_EQ(0, ioctx.pool_requires_alignment2(&requires));
+  ASSERT_FALSE(requires);
+}
+
+void RadosTestParamPP::TearDown()
+{
+  if (cleanup) {
+    cleanup_default_namespace(ioctx);
+    cleanup_namespace(ioctx, nspace);
+  }
+  ioctx.close();
+}
+
+void RadosTestParamPP::cleanup_default_namespace(librados::IoCtx ioctx)
+{
+  // remove all objects from the default namespace to avoid polluting
+  // other tests
+  cleanup_namespace(ioctx, "");
+}
+
+void RadosTestParamPP::cleanup_namespace(librados::IoCtx ioctx, std::string ns)
+{
+  ioctx.snap_set_read(librados::SNAP_HEAD);
+  ioctx.set_namespace(ns);
+  for (NObjectIterator it = ioctx.nobjects_begin();
+       it != ioctx.nobjects_end(); ++it) {
+    ioctx.locator_set_key(it->get_locator());
+    ASSERT_EQ(0, ioctx.remove(it->get_oid()));
+  }
+}
+
+std::string RadosTestECPP::pool_name;
+Rados RadosTestECPP::s_cluster;
+
+void RadosTestECPP::SetUpTestCase()
+{
+  pool_name = get_temp_pool_name();
+  ASSERT_EQ("", create_one_ec_pool_pp(pool_name, s_cluster));
+}
+
+void RadosTestECPP::TearDownTestCase()
+{
+  ASSERT_EQ(0, destroy_one_ec_pool_pp(pool_name, s_cluster));
+}
+
+void RadosTestECPP::SetUp()
+{
+  ASSERT_EQ(0, cluster.ioctx_create(pool_name.c_str(), ioctx));
+  nspace = get_temp_pool_name();
+  ioctx.set_namespace(nspace);
+  bool requires;
+  ASSERT_EQ(0, ioctx.pool_requires_alignment2(&requires));
+  ASSERT_TRUE(requires);
+  ASSERT_EQ(0, ioctx.pool_required_alignment2(&alignment));
+  ASSERT_NE(0U, alignment);
+}
+
+void RadosTestECPP::TearDown()
+{
+  if (cleanup) {
+    cleanup_default_namespace(ioctx);
+    cleanup_namespace(ioctx, nspace);
+  }
+  ioctx.close();
+}
+
diff --git a/src/test/librados/testcase_cxx.h b/src/test/librados/testcase_cxx.h
new file mode 100644 (file)
index 0000000..637ec11
--- /dev/null
@@ -0,0 +1,130 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#pragma once
+
+#include "gtest/gtest.h"
+#include "include/rados/librados.hpp"
+
+class RadosTestPPNS : public ::testing::Test {
+public:
+  RadosTestPPNS(bool c=false) : cluster(s_cluster), cleanup(c) {}
+  ~RadosTestPPNS() override {}
+protected:
+  static void SetUpTestCase();
+  static void TearDownTestCase();
+  static void cleanup_all_objects(librados::IoCtx ioctx);
+  static librados::Rados s_cluster;
+  static std::string pool_name;
+
+  void SetUp() override;
+  void TearDown() override;
+  librados::Rados &cluster;
+  librados::IoCtx ioctx;
+  bool cleanup;
+};
+
+struct RadosTestPPNSCleanup : public RadosTestPPNS {
+  RadosTestPPNSCleanup() : RadosTestPPNS(true) {}
+};
+
+class RadosTestParamPPNS : public ::testing::TestWithParam<const char*> {
+public:
+  RadosTestParamPPNS(bool c=false) : cluster(s_cluster), cleanup(c) {}
+  ~RadosTestParamPPNS() override {}
+  static void SetUpTestCase();
+  static void TearDownTestCase();
+protected:
+  static void cleanup_all_objects(librados::IoCtx ioctx);
+  static librados::Rados s_cluster;
+  static std::string pool_name;
+  static std::string cache_pool_name;
+
+  void SetUp() override;
+  void TearDown() override;
+  librados::Rados &cluster;
+  librados::IoCtx ioctx;
+  bool cleanup;
+};
+
+class RadosTestECPPNS : public RadosTestPPNS {
+public:
+  RadosTestECPPNS(bool c=false) : cluster(s_cluster), cleanup(c) {}
+  ~RadosTestECPPNS() override {}
+protected:
+  static void SetUpTestCase();
+  static void TearDownTestCase();
+  static librados::Rados s_cluster;
+  static std::string pool_name;
+
+  void SetUp() override;
+  void TearDown() override;
+  librados::Rados &cluster;
+  librados::IoCtx ioctx;
+  uint64_t alignment = 0;
+  bool cleanup;
+};
+
+struct RadosTestECPPNSCleanup : public RadosTestECPPNS {
+  RadosTestECPPNSCleanup() : RadosTestECPPNS(true) {}
+};
+
+class RadosTestPP : public ::testing::Test {
+public:
+  RadosTestPP(bool c=false) : cluster(s_cluster), cleanup(c) {}
+  ~RadosTestPP() override {}
+protected:
+  static void SetUpTestCase();
+  static void TearDownTestCase();
+  static void cleanup_default_namespace(librados::IoCtx ioctx);
+  static void cleanup_namespace(librados::IoCtx ioctx, std::string ns);
+  static librados::Rados s_cluster;
+  static std::string pool_name;
+
+  void SetUp() override;
+  void TearDown() override;
+  librados::Rados &cluster;
+  librados::IoCtx ioctx;
+  bool cleanup;
+  std::string nspace;
+};
+
+class RadosTestParamPP : public ::testing::TestWithParam<const char*> {
+public:
+  RadosTestParamPP(bool c=false) : cluster(s_cluster), cleanup(c) {}
+  ~RadosTestParamPP() override {}
+  static void SetUpTestCase();
+  static void TearDownTestCase();
+protected:
+  static void cleanup_default_namespace(librados::IoCtx ioctx);
+  static void cleanup_namespace(librados::IoCtx ioctx, std::string ns);
+  static librados::Rados s_cluster;
+  static std::string pool_name;
+  static std::string cache_pool_name;
+
+  void SetUp() override;
+  void TearDown() override;
+  librados::Rados &cluster;
+  librados::IoCtx ioctx;
+  bool cleanup;
+  std::string nspace;
+};
+
+class RadosTestECPP : public RadosTestPP {
+public:
+  RadosTestECPP(bool c=false) : cluster(s_cluster), cleanup(c) {}
+  ~RadosTestECPP() override {}
+protected:
+  static void SetUpTestCase();
+  static void TearDownTestCase();
+  static librados::Rados s_cluster;
+  static std::string pool_name;
+
+  void SetUp() override;
+  void TearDown() override;
+  librados::Rados &cluster;
+  librados::IoCtx ioctx;
+  bool cleanup;
+  std::string nspace;
+  uint64_t alignment = 0;
+};
diff --git a/src/test/librados/tier.cc b/src/test/librados/tier.cc
deleted file mode 100644 (file)
index d7f30e6..0000000
+++ /dev/null
@@ -1,6490 +0,0 @@
-// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
-// vim: ts=8 sw=2 smarttab
-#include "gtest/gtest.h"
-
-#include "mds/mdstypes.h"
-#include "include/buffer.h"
-#include "include/rbd_types.h"
-#include "include/rados/librados.h"
-#include "include/rados/librados.hpp"
-#include "include/stringify.h"
-#include "include/types.h"
-#include "global/global_context.h"
-#include "common/Cond.h"
-#include "test/librados/test.h"
-#include "test/librados/TestCase.h"
-#include "json_spirit/json_spirit.h"
-#include "cls/cas/cls_cas_ops.h"
-
-#include "osd/HitSet.h"
-
-#include <errno.h>
-#include <map>
-#include <sstream>
-#include <string>
-
-using namespace librados;
-using std::map;
-using std::ostringstream;
-using std::string;
-
-typedef RadosTestPP LibRadosTierPP;
-typedef RadosTestECPP LibRadosTierECPP;
-
-void flush_evict_all(librados::Rados& cluster, librados::IoCtx& cache_ioctx)
-{
-  bufferlist inbl;
-  cache_ioctx.set_namespace(all_nspaces);
-  for (NObjectIterator it = cache_ioctx.nobjects_begin();
-       it != cache_ioctx.nobjects_end(); ++it) {
-    cache_ioctx.locator_set_key(it->get_locator());
-    cache_ioctx.set_namespace(it->get_nspace());
-    {
-      ObjectReadOperation op;
-      op.cache_flush();
-      librados::AioCompletion *completion = cluster.aio_create_completion();
-      cache_ioctx.aio_operate(
-        it->get_oid(), completion, &op,
-       librados::OPERATION_IGNORE_OVERLAY, NULL);
-      completion->wait_for_safe();
-      completion->get_return_value();
-      completion->release();
-    }
-    {
-      ObjectReadOperation op;
-      op.cache_evict();
-      librados::AioCompletion *completion = cluster.aio_create_completion();
-      cache_ioctx.aio_operate(
-        it->get_oid(), completion, &op,
-       librados::OPERATION_IGNORE_OVERLAY, NULL);
-      completion->wait_for_safe();
-      completion->get_return_value();
-      completion->release();
-    }
-  }
-}
-
-class LibRadosTwoPoolsPP : public RadosTestPP
-{
-public:
-  LibRadosTwoPoolsPP() {};
-  ~LibRadosTwoPoolsPP() override {};
-protected:
-  static void SetUpTestCase() {
-    pool_name = get_temp_pool_name();
-    ASSERT_EQ("", create_one_pool_pp(pool_name, s_cluster));
-  }
-  static void TearDownTestCase() {
-    ASSERT_EQ(0, destroy_one_pool_pp(pool_name, s_cluster));
-  }
-  static std::string cache_pool_name;
-
-  void SetUp() override {
-    cache_pool_name = get_temp_pool_name();
-    ASSERT_EQ(0, s_cluster.pool_create(cache_pool_name.c_str()));
-    RadosTestPP::SetUp();
-
-    ASSERT_EQ(0, cluster.ioctx_create(cache_pool_name.c_str(), cache_ioctx));
-    cache_ioctx.application_enable("rados", true);
-    cache_ioctx.set_namespace(nspace);
-  }
-  void TearDown() override {
-    // flush + evict cache
-    flush_evict_all(cluster, cache_ioctx);
-
-    bufferlist inbl;
-    // tear down tiers
-    ASSERT_EQ(0, cluster.mon_command(
-      "{\"prefix\": \"osd tier remove-overlay\", \"pool\": \"" + pool_name +
-      "\"}",
-      inbl, NULL, NULL));
-    ASSERT_EQ(0, cluster.mon_command(
-      "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
-      "\", \"tierpool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-
-    // wait for maps to settle before next test
-    cluster.wait_for_latest_osdmap();
-
-    RadosTestPP::TearDown();
-
-    cleanup_default_namespace(cache_ioctx);
-    cleanup_namespace(cache_ioctx, nspace);
-
-    cache_ioctx.close();
-    ASSERT_EQ(0, s_cluster.pool_delete(cache_pool_name.c_str()));
-  }
-  librados::IoCtx cache_ioctx;
-};
-
-class Completions
-{
-public:
-  Completions() = default;
-  librados::AioCompletion* getCompletion() {
-    librados::AioCompletion* comp = librados::Rados::aio_create_completion();
-    m_completions.push_back(comp);
-    return comp;
-  }
-
-  ~Completions() {
-    for (auto& comp : m_completions) {
-      comp->release();
-    }
-  }
-
-private:
-  vector<librados::AioCompletion *> m_completions;
-};
-
-Completions completions;
-
-std::string LibRadosTwoPoolsPP::cache_pool_name;
-
-TEST_F(LibRadosTierPP, Dirty) {
-  {
-    ObjectWriteOperation op;
-    op.undirty();
-    ASSERT_EQ(0, ioctx.operate("foo", &op)); // still get 0 if it dne
-  }
-  {
-    ObjectWriteOperation op;
-    op.create(true);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-  {
-    bool dirty = false;
-    int r = -1;
-    ObjectReadOperation op;
-    op.is_dirty(&dirty, &r);
-    ASSERT_EQ(0, ioctx.operate("foo", &op, NULL));
-    ASSERT_TRUE(dirty);
-    ASSERT_EQ(0, r);
-  }
-  {
-    ObjectWriteOperation op;
-    op.undirty();
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-  {
-    ObjectWriteOperation op;
-    op.undirty();
-    ASSERT_EQ(0, ioctx.operate("foo", &op));  // still 0 if already clean
-  }
-  {
-    bool dirty = false;
-    int r = -1;
-    ObjectReadOperation op;
-    op.is_dirty(&dirty, &r);
-    ASSERT_EQ(0, ioctx.operate("foo", &op, NULL));
-    ASSERT_FALSE(dirty);
-    ASSERT_EQ(0, r);
-  }
-  {
-    ObjectWriteOperation op;
-    op.truncate(0);  // still a write even tho it is a no-op
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-  {
-    bool dirty = false;
-    int r = -1;
-    ObjectReadOperation op;
-    op.is_dirty(&dirty, &r);
-    ASSERT_EQ(0, ioctx.operate("foo", &op, NULL));
-    ASSERT_TRUE(dirty);
-    ASSERT_EQ(0, r);
-  }
-}
-
-TEST_F(LibRadosTwoPoolsPP, Overlay) {
-  // create objects
-  {
-    bufferlist bl;
-    bl.append("base");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("cache");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, cache_ioctx.operate("foo", &op));
-  }
-
-  // configure cache
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name +
-    "\", \"force_nonempty\": \"--force-nonempty\" }",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
-    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  // by default, the overlay sends us to cache pool
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
-    ASSERT_EQ('c', bl[0]);
-  }
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, cache_ioctx.read("foo", bl, 1, 0));
-    ASSERT_EQ('c', bl[0]);
-  }
-
-  // unless we say otherwise
-  {
-    bufferlist bl;
-    ObjectReadOperation op;
-    op.read(0, 1, &bl, NULL);
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate(
-       "foo", completion, &op,
-       librados::OPERATION_IGNORE_OVERLAY, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-    ASSERT_EQ('b', bl[0]);
-  }
-}
-
-TEST_F(LibRadosTwoPoolsPP, Promote) {
-  // create object
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-
-  // configure cache
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name +
-    "\", \"force_nonempty\": \"--force-nonempty\" }",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
-    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
-    "\", \"mode\": \"writeback\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  // read, trigger a promote
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
-  }
-
-  // read, trigger a whiteout
-  {
-    bufferlist bl;
-    ASSERT_EQ(-ENOENT, ioctx.read("bar", bl, 1, 0));
-    ASSERT_EQ(-ENOENT, ioctx.read("bar", bl, 1, 0));
-  }
-
-  // verify the object is present in the cache tier
-  {
-    NObjectIterator it = cache_ioctx.nobjects_begin();
-    ASSERT_TRUE(it != cache_ioctx.nobjects_end());
-    ASSERT_TRUE(it->get_oid() == string("foo") || it->get_oid() == string("bar"));
-    ++it;
-    ASSERT_TRUE(it->get_oid() == string("foo") || it->get_oid() == string("bar"));
-    ++it;
-    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
-  }
-}
-
-TEST_F(LibRadosTwoPoolsPP, PromoteSnap) {
-  // create object
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("bar", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("baz", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("bam", &op));
-  }
-
-  // create a snapshot, clone
-  vector<uint64_t> my_snaps(1);
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
-                                                        my_snaps));
-  {
-    bufferlist bl;
-    bl.append("ciao!");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("ciao!");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("bar", &op));
-  }
-  {
-    ObjectWriteOperation op;
-    op.remove();
-    ASSERT_EQ(0, ioctx.operate("baz", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("ciao!");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("bam", &op));
-  }
-
-  // configure cache
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name +
-    "\", \"force_nonempty\": \"--force-nonempty\" }",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
-    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
-    "\", \"mode\": \"writeback\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  // read, trigger a promote on the head
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
-    ASSERT_EQ('c', bl[0]);
-  }
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("bam", bl, 1, 0));
-    ASSERT_EQ('c', bl[0]);
-  }
-
-  ioctx.snap_set_read(my_snaps[0]);
-
-  // read foo snap
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
-    ASSERT_EQ('h', bl[0]);
-  }
-
-  // read bar snap
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("bar", bl, 1, 0));
-    ASSERT_EQ('h', bl[0]);
-  }
-
-  // read baz snap
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("baz", bl, 1, 0));
-    ASSERT_EQ('h', bl[0]);
-  }
-
-  ioctx.snap_set_read(librados::SNAP_HEAD);
-
-  // read foo
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
-    ASSERT_EQ('c', bl[0]);
-  }
-
-  // read bar
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("bar", bl, 1, 0));
-    ASSERT_EQ('c', bl[0]);
-  }
-
-  // read baz
-  {
-    bufferlist bl;
-    ASSERT_EQ(-ENOENT, ioctx.read("baz", bl, 1, 0));
-  }
-
-  // cleanup
-  ioctx.selfmanaged_snap_remove(my_snaps[0]);
-}
-
-TEST_F(LibRadosTwoPoolsPP, PromoteSnapScrub) {
-  int num = 100;
-
-  // create objects
-  for (int i=0; i<num; ++i) {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate(string("foo") + stringify(i), &op));
-  }
-
-  vector<uint64_t> my_snaps;
-  for (int snap=0; snap<4; ++snap) {
-    // create a snapshot, clone
-    vector<uint64_t> ns(1);
-    ns.insert(ns.end(), my_snaps.begin(), my_snaps.end());
-    my_snaps.swap(ns);
-    ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
-    cout << "my_snaps " << my_snaps << std::endl;
-    ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
-                                                     my_snaps));
-    for (int i=0; i<num; ++i) {
-      bufferlist bl;
-      bl.append(string("ciao! snap") + stringify(snap));
-      ObjectWriteOperation op;
-      op.write_full(bl);
-      ASSERT_EQ(0, ioctx.operate(string("foo") + stringify(i), &op));
-    }
-  }
-
-  // configure cache
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name +
-    "\", \"force_nonempty\": \"--force-nonempty\" }",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
-    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
-    "\", \"mode\": \"writeback\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  // read, trigger a promote on _some_ heads to make sure we handle cases
-  // where snaps are present and where they are not.
-  cout << "promoting some heads" << std::endl;
-  for (int i=0; i<num; ++i) {
-    if (i % 5 == 0 || i > num - 3) {
-      bufferlist bl;
-      ASSERT_EQ(1, ioctx.read(string("foo") + stringify(i), bl, 1, 0));
-      ASSERT_EQ('c', bl[0]);
-    }
-  }
-
-  for (unsigned snap = 0; snap < my_snaps.size(); ++snap) {
-    cout << "promoting from clones for snap " << my_snaps[snap] << std::endl;
-    ioctx.snap_set_read(my_snaps[snap]);
-
-    // read some snaps, semi-randomly
-    for (int i=0; i<50; ++i) {
-      bufferlist bl;
-      string o = string("foo") + stringify((snap * i * 137) % 80);
-      //cout << o << std::endl;
-      ASSERT_EQ(1, ioctx.read(o, bl, 1, 0));
-    }
-  }
-
-  // ok, stop and scrub this pool (to make sure scrub can handle
-  // missing clones in the cache tier).
-  {
-    IoCtx cache_ioctx;
-    ASSERT_EQ(0, cluster.ioctx_create(cache_pool_name.c_str(), cache_ioctx));
-    for (int i=0; i<10; ++i) {
-      do {
-       ostringstream ss;
-       ss << "{\"prefix\": \"pg scrub\", \"pgid\": \""
-          << cache_ioctx.get_id() << "." << i
-          << "\"}";
-       int r = cluster.mon_command(ss.str(), inbl, NULL, NULL);
-       if (r == -ENOENT ||  // in case mgr osdmap is stale
-           r == -EAGAIN) {
-         sleep(5);
-         continue;
-       }
-      } while (false);
-    }
-
-    // give it a few seconds to go.  this is sloppy but is usually enough time
-    cout << "waiting for scrubs..." << std::endl;
-    sleep(30);
-    cout << "done waiting" << std::endl;
-  }
-
-  ioctx.snap_set_read(librados::SNAP_HEAD);
-
-  //cleanup
-  for (unsigned snap = 0; snap < my_snaps.size(); ++snap) {
-    ioctx.selfmanaged_snap_remove(my_snaps[snap]);
-  }
-}
-
-TEST_F(LibRadosTwoPoolsPP, PromoteSnapTrimRace) {
-  // create object
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-
-  // create a snapshot, clone
-  vector<uint64_t> my_snaps(1);
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
-                                                        my_snaps));
-  {
-    bufferlist bl;
-    bl.append("ciao!");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-
-  // configure cache
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name +
-    "\", \"force_nonempty\": \"--force-nonempty\" }",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
-    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
-    "\", \"mode\": \"writeback\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  // delete the snap
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_remove(my_snaps[0]));
-
-  ioctx.snap_set_read(my_snaps[0]);
-
-  // read foo snap
-  {
-    bufferlist bl;
-    ASSERT_EQ(-ENOENT, ioctx.read("foo", bl, 1, 0));
-  }
-
-  // cleanup
-  ioctx.selfmanaged_snap_remove(my_snaps[0]);
-}
-
-TEST_F(LibRadosTwoPoolsPP, Whiteout) {
-  // create object
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-
-  // configure cache
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name +
-    "\", \"force_nonempty\": \"--force-nonempty\" }",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
-    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
-    "\", \"mode\": \"writeback\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  // create some whiteouts, verify they behave
-  {
-    ObjectWriteOperation op;
-    op.assert_exists();
-    op.remove();
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-
-  {
-    ObjectWriteOperation op;
-    op.assert_exists();
-    op.remove();
-    ASSERT_EQ(-ENOENT, ioctx.operate("bar", &op));
-  }
-  {
-    ObjectWriteOperation op;
-    op.assert_exists();
-    op.remove();
-    ASSERT_EQ(-ENOENT, ioctx.operate("bar", &op));
-  }
-
-  // verify the whiteouts are there in the cache tier
-  {
-    NObjectIterator it = cache_ioctx.nobjects_begin();
-    ASSERT_TRUE(it != cache_ioctx.nobjects_end());
-    ASSERT_TRUE(it->get_oid() == string("foo") || it->get_oid() == string("bar"));
-    ++it;
-    ASSERT_TRUE(it->get_oid() == string("foo") || it->get_oid() == string("bar"));
-    ++it;
-    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
-  }
-
-  // delete a whiteout and verify it goes away
-  ASSERT_EQ(-ENOENT, ioctx.remove("foo"));
-  {
-    ObjectWriteOperation op;
-    op.remove();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate("bar", completion, &op,
-                                  librados::OPERATION_IGNORE_CACHE));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-
-    NObjectIterator it = cache_ioctx.nobjects_begin();
-    ASSERT_TRUE(it != cache_ioctx.nobjects_end());
-    ASSERT_TRUE(it->get_oid() == string("foo"));
-    ++it;
-    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
-  }
-
-  // recreate an object and verify we can read it
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
-    ASSERT_EQ('h', bl[0]);
-  }
-}
-
-TEST_F(LibRadosTwoPoolsPP, WhiteoutDeleteCreate) {
-  // configure cache
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name +
-    "\", \"force_nonempty\": \"--force-nonempty\" }",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
-    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
-    "\", \"mode\": \"writeback\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  // create an object
-  {
-    bufferlist bl;
-    bl.append("foo");
-    ASSERT_EQ(0, ioctx.write_full("foo", bl));
-  }
-
-  // do delete + create operation
-  {
-    ObjectWriteOperation op;
-    op.remove();
-    bufferlist bl;
-    bl.append("bar");
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-
-  // verify it still "exists" (w/ new content)
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
-    ASSERT_EQ('b', bl[0]);
-  }
-}
-
-TEST_F(LibRadosTwoPoolsPP, Evict) {
-  // create object
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-
-  // configure cache
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name +
-    "\", \"force_nonempty\": \"--force-nonempty\" }",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
-    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
-    "\", \"mode\": \"writeback\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  // read, trigger a promote
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
-  }
-
-  // read, trigger a whiteout, and a dirty object
-  {
-    bufferlist bl;
-    ASSERT_EQ(-ENOENT, ioctx.read("bar", bl, 1, 0));
-    ASSERT_EQ(-ENOENT, ioctx.read("bar", bl, 1, 0));
-    ASSERT_EQ(0, ioctx.write("bar", bl, bl.length(), 0));
-  }
-
-  // verify the object is present in the cache tier
-  {
-    NObjectIterator it = cache_ioctx.nobjects_begin();
-    ASSERT_TRUE(it != cache_ioctx.nobjects_end());
-    ASSERT_TRUE(it->get_oid() == string("foo") || it->get_oid() == string("bar"));
-    ++it;
-    ASSERT_TRUE(it->get_oid() == string("foo") || it->get_oid() == string("bar"));
-    ++it;
-    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
-  }
-
-  // pin
-  {
-    ObjectWriteOperation op;
-    op.cache_pin();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-
-  // evict the pinned object with -EPERM
-  {
-    ObjectReadOperation op;
-    op.cache_evict();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op,
-                                        librados::OPERATION_IGNORE_CACHE,
-                                        NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(-EPERM, completion->get_return_value());
-    completion->release();
-  }
-
-  // unpin
-  {
-    ObjectWriteOperation op;
-    op.cache_unpin();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-
-  // flush
-  {
-    ObjectReadOperation op;
-    op.cache_flush();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate(
-      "foo", completion, &op,
-      librados::OPERATION_IGNORE_OVERLAY, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-
-  // verify clean
-  {
-    bool dirty = false;
-    int r = -1;
-    ObjectReadOperation op;
-    op.is_dirty(&dirty, &r);
-    ASSERT_EQ(0, cache_ioctx.operate("foo", &op, NULL));
-    ASSERT_FALSE(dirty);
-    ASSERT_EQ(0, r);
-  }
-
-  // evict
-  {
-    ObjectReadOperation op;
-    op.cache_evict();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op,
-                                        librados::OPERATION_IGNORE_CACHE,
-                                        NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-  {
-    ObjectReadOperation op;
-    op.cache_evict();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate(
-      "foo", completion, &op,
-      librados::OPERATION_IGNORE_CACHE, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-  {
-    ObjectReadOperation op;
-    op.cache_evict();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate(
-      "bar", completion, &op,
-      librados::OPERATION_IGNORE_CACHE, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(-EBUSY, completion->get_return_value());
-    completion->release();
-  }
-}
-
-TEST_F(LibRadosTwoPoolsPP, EvictSnap) {
-  // create object
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("bar", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("baz", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("bam", &op));
-  }
-
-  // create a snapshot, clone
-  vector<uint64_t> my_snaps(1);
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
-                                                        my_snaps));
-  {
-    bufferlist bl;
-    bl.append("ciao!");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("ciao!");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("bar", &op));
-  }
-  {
-    ObjectWriteOperation op;
-    op.remove();
-    ASSERT_EQ(0, ioctx.operate("baz", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("ciao!");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("bam", &op));
-  }
-
-  // configure cache
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name +
-    "\", \"force_nonempty\": \"--force-nonempty\" }",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
-    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
-    "\", \"mode\": \"writeback\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  // read, trigger a promote on the head
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
-    ASSERT_EQ('c', bl[0]);
-  }
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("bam", bl, 1, 0));
-    ASSERT_EQ('c', bl[0]);
-  }
-
-  // evict bam
-  {
-    ObjectReadOperation op;
-    op.cache_evict();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate(
-      "bam", completion, &op,
-      librados::OPERATION_IGNORE_CACHE, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-  {
-    bufferlist bl;
-    ObjectReadOperation op;
-    op.read(1, 0, &bl, NULL);
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate(
-      "bam", completion, &op,
-      librados::OPERATION_IGNORE_CACHE, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(-ENOENT, completion->get_return_value());
-    completion->release();
-  }
-
-  // read foo snap
-  ioctx.snap_set_read(my_snaps[0]);
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
-    ASSERT_EQ('h', bl[0]);
-  }
-
-  // evict foo snap
-  {
-    ObjectReadOperation op;
-    op.cache_evict();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate(
-      "foo", completion, &op,
-      librados::OPERATION_IGNORE_CACHE, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-  // snap is gone...
-  {
-    bufferlist bl;
-    ObjectReadOperation op;
-    op.read(1, 0, &bl, NULL);
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate(
-      "foo", completion, &op,
-      librados::OPERATION_IGNORE_CACHE, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(-ENOENT, completion->get_return_value());
-    completion->release();
-  }
-  // head is still there...
-  ioctx.snap_set_read(librados::SNAP_HEAD);
-  {
-    bufferlist bl;
-    ObjectReadOperation op;
-    op.read(1, 0, &bl, NULL);
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate(
-      "foo", completion, &op,
-      librados::OPERATION_IGNORE_CACHE, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-
-  // promote head + snap of bar
-  ioctx.snap_set_read(librados::SNAP_HEAD);
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("bar", bl, 1, 0));
-    ASSERT_EQ('c', bl[0]);
-  }
-  ioctx.snap_set_read(my_snaps[0]);
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("bar", bl, 1, 0));
-    ASSERT_EQ('h', bl[0]);
-  }
-
-  // evict bar head (fail)
-  ioctx.snap_set_read(librados::SNAP_HEAD);
-  {
-    ObjectReadOperation op;
-    op.cache_evict();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate(
-      "bar", completion, &op,
-      librados::OPERATION_IGNORE_CACHE, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(-EBUSY, completion->get_return_value());
-    completion->release();
-  }
-
-  // evict bar snap
-  ioctx.snap_set_read(my_snaps[0]);
-  {
-    ObjectReadOperation op;
-    op.cache_evict();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate(
-      "bar", completion, &op,
-      librados::OPERATION_IGNORE_CACHE, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-  // ...and then head
-  ioctx.snap_set_read(librados::SNAP_HEAD);
-  {
-    bufferlist bl;
-    ObjectReadOperation op;
-    op.read(1, 0, &bl, NULL);
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate(
-      "bar", completion, &op,
-      librados::OPERATION_IGNORE_CACHE, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-  {
-    ObjectReadOperation op;
-    op.cache_evict();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate(
-      "bar", completion, &op,
-      librados::OPERATION_IGNORE_CACHE, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-
-  // cleanup
-  ioctx.selfmanaged_snap_remove(my_snaps[0]);
-}
-
-// this test case reproduces http://tracker.ceph.com/issues/8629
-TEST_F(LibRadosTwoPoolsPP, EvictSnap2) {
-  // create object
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-  // create a snapshot, clone
-  vector<uint64_t> my_snaps(1);
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
-                                                        my_snaps));
-  {
-    bufferlist bl;
-    bl.append("ciao!");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-  // configure cache
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name +
-    "\", \"force_nonempty\": \"--force-nonempty\" }",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
-    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
-    "\", \"mode\": \"writeback\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  // read, trigger a promote on the head
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
-    ASSERT_EQ('c', bl[0]);
-  }
-
-  // evict
-  {
-    ObjectReadOperation op;
-    op.cache_evict();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate(
-      "foo", completion, &op,
-      librados::OPERATION_IGNORE_CACHE, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-
-  // verify the snapdir is not present in the cache pool
-  {
-    ObjectReadOperation op;
-    librados::snap_set_t snapset;
-    op.list_snaps(&snapset, NULL);
-    ioctx.snap_set_read(librados::SNAP_DIR);
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate("foo", completion, &op,
-                                  librados::OPERATION_IGNORE_CACHE, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(-ENOENT, completion->get_return_value());
-    completion->release();
-  }
-}
-
-//This test case reproduces http://tracker.ceph.com/issues/17445
-TEST_F(LibRadosTwoPoolsPP, ListSnap){
-  // Create object
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("bar", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("baz", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("bam", &op));
-  }
-
-  // Create a snapshot, clone
-  vector<uint64_t> my_snaps(1);
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
-                                                        my_snaps));
-  {
-    bufferlist bl;
-    bl.append("ciao!");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("ciao!");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("bar", &op));
-  }
-  {
-    ObjectWriteOperation op;
-    op.remove();
-    ASSERT_EQ(0, ioctx.operate("baz", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("ciao!");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("bam", &op));
-  }
-
-  // Configure cache
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name +
-    "\", \"force_nonempty\": \"--force-nonempty\" }",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
-    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
-    "\", \"mode\": \"writeback\"}",
-    inbl, NULL, NULL));
-
-  // Wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  // Read, trigger a promote on the head
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
-    ASSERT_EQ('c', bl[0]);
-  }
-
-  // Read foo snap
-  ioctx.snap_set_read(my_snaps[0]);
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
-    ASSERT_EQ('h', bl[0]);
-  }
-
-  // Evict foo snap
-  {
-    ObjectReadOperation op;
-    op.cache_evict();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate(
-      "foo", completion, &op,
-      librados::OPERATION_IGNORE_CACHE, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-  // Snap is gone...
-  {
-    bufferlist bl;
-    ObjectReadOperation op;
-    op.read(1, 0, &bl, NULL);
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate(
-      "foo", completion, &op,
-      librados::OPERATION_IGNORE_CACHE, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(-ENOENT, completion->get_return_value());
-    completion->release();
-  }
-
-  // Do list-snaps
-  ioctx.snap_set_read(CEPH_SNAPDIR);
-  {
-    snap_set_t snap_set;
-    int snap_ret;
-    ObjectReadOperation op;
-    op.list_snaps(&snap_set, &snap_ret);
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate(
-      "foo", completion, &op,
-      0, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, snap_ret);
-    ASSERT_LT(0u, snap_set.clones.size());
-    for (vector<librados::clone_info_t>::const_iterator r = snap_set.clones.begin();
-       r != snap_set.clones.end();
-       ++r) {
-      if (r->cloneid != librados::SNAP_HEAD) {
-       ASSERT_LT(0u, r->snaps.size());
-      }
-    }
-  }
-
-  // Cleanup
-  ioctx.selfmanaged_snap_remove(my_snaps[0]);
-}
-
-TEST_F(LibRadosTwoPoolsPP, TryFlush) {
-  // configure cache
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name +
-    "\", \"force_nonempty\": \"--force-nonempty\" }",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
-    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
-    "\", \"mode\": \"writeback\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  // create object
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-
-  // verify the object is present in the cache tier
-  {
-    NObjectIterator it = cache_ioctx.nobjects_begin();
-    ASSERT_TRUE(it != cache_ioctx.nobjects_end());
-    ASSERT_TRUE(it->get_oid() == string("foo"));
-    ++it;
-    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
-  }
-
-  // verify the object is NOT present in the base tier
-  {
-    NObjectIterator it = ioctx.nobjects_begin();
-    ASSERT_TRUE(it == ioctx.nobjects_end());
-  }
-
-  // verify dirty
-  {
-    bool dirty = false;
-    int r = -1;
-    ObjectReadOperation op;
-    op.is_dirty(&dirty, &r);
-    ASSERT_EQ(0, cache_ioctx.operate("foo", &op, NULL));
-    ASSERT_TRUE(dirty);
-    ASSERT_EQ(0, r);
-  }
-
-  // pin
-  {
-    ObjectWriteOperation op;
-    op.cache_pin();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-
-  // flush the pinned object with -EPERM
-  {
-    ObjectReadOperation op;
-    op.cache_try_flush();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate(
-      "foo", completion, &op,
-      librados::OPERATION_IGNORE_OVERLAY |
-      librados::OPERATION_SKIPRWLOCKS, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(-EPERM, completion->get_return_value());
-    completion->release();
-  }
-
-  // unpin
-  {
-    ObjectWriteOperation op;
-    op.cache_unpin();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-
-  // flush
-  {
-    ObjectReadOperation op;
-    op.cache_try_flush();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate(
-      "foo", completion, &op,
-      librados::OPERATION_IGNORE_OVERLAY |
-      librados::OPERATION_SKIPRWLOCKS, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-
-  // verify clean
-  {
-    bool dirty = false;
-    int r = -1;
-    ObjectReadOperation op;
-    op.is_dirty(&dirty, &r);
-    ASSERT_EQ(0, cache_ioctx.operate("foo", &op, NULL));
-    ASSERT_FALSE(dirty);
-    ASSERT_EQ(0, r);
-  }
-
-  // verify in base tier
-  {
-    NObjectIterator it = ioctx.nobjects_begin();
-    ASSERT_TRUE(it != ioctx.nobjects_end());
-    ASSERT_TRUE(it->get_oid() == string("foo"));
-    ++it;
-    ASSERT_TRUE(it == ioctx.nobjects_end());
-  }
-
-  // evict it
-  {
-    ObjectReadOperation op;
-    op.cache_evict();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate(
-        "foo", completion, &op, librados::OPERATION_IGNORE_CACHE, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-
-  // verify no longer in cache tier
-  {
-    NObjectIterator it = cache_ioctx.nobjects_begin();
-    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
-  }
-}
-
-TEST_F(LibRadosTwoPoolsPP, Flush) {
-  // configure cache
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name +
-    "\", \"force_nonempty\": \"--force-nonempty\" }",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
-    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
-    "\", \"mode\": \"writeback\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  uint64_t user_version = 0;
-
-  // create object
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-
-  // verify the object is present in the cache tier
-  {
-    NObjectIterator it = cache_ioctx.nobjects_begin();
-    ASSERT_TRUE(it != cache_ioctx.nobjects_end());
-    ASSERT_TRUE(it->get_oid() == string("foo"));
-    ++it;
-    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
-  }
-
-  // verify the object is NOT present in the base tier
-  {
-    NObjectIterator it = ioctx.nobjects_begin();
-    ASSERT_TRUE(it == ioctx.nobjects_end());
-  }
-
-  // verify dirty
-  {
-    bool dirty = false;
-    int r = -1;
-    ObjectReadOperation op;
-    op.is_dirty(&dirty, &r);
-    ASSERT_EQ(0, cache_ioctx.operate("foo", &op, NULL));
-    ASSERT_TRUE(dirty);
-    ASSERT_EQ(0, r);
-    user_version = cache_ioctx.get_last_version();
-  }
-
-  // pin
-  {
-    ObjectWriteOperation op;
-    op.cache_pin();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-
-  // flush the pinned object with -EPERM
-  {
-    ObjectReadOperation op;
-    op.cache_try_flush();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate(
-      "foo", completion, &op,
-      librados::OPERATION_IGNORE_OVERLAY |
-      librados::OPERATION_SKIPRWLOCKS, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(-EPERM, completion->get_return_value());
-    completion->release();
-  }
-
-  // unpin
-  {
-    ObjectWriteOperation op;
-    op.cache_unpin();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-
-  // flush
-  {
-    ObjectReadOperation op;
-    op.cache_flush();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate(
-      "foo", completion, &op,
-      librados::OPERATION_IGNORE_OVERLAY, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-
-  // verify clean
-  {
-    bool dirty = false;
-    int r = -1;
-    ObjectReadOperation op;
-    op.is_dirty(&dirty, &r);
-    ASSERT_EQ(0, cache_ioctx.operate("foo", &op, NULL));
-    ASSERT_FALSE(dirty);
-    ASSERT_EQ(0, r);
-  }
-
-  // verify in base tier
-  {
-    NObjectIterator it = ioctx.nobjects_begin();
-    ASSERT_TRUE(it != ioctx.nobjects_end());
-    ASSERT_TRUE(it->get_oid() == string("foo"));
-    ++it;
-    ASSERT_TRUE(it == ioctx.nobjects_end());
-  }
-
-  // evict it
-  {
-    ObjectReadOperation op;
-    op.cache_evict();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate(
-        "foo", completion, &op, librados::OPERATION_IGNORE_CACHE, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-
-  // verify no longer in cache tier
-  {
-    NObjectIterator it = cache_ioctx.nobjects_begin();
-    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
-  }
-
-  // read it again and verify the version is consistent
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, cache_ioctx.read("foo", bl, 1, 0));
-    ASSERT_EQ(user_version, cache_ioctx.get_last_version());
-  }
-
-  // erase it
-  {
-    ObjectWriteOperation op;
-    op.remove();
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-
-  // flush whiteout
-  {
-    ObjectReadOperation op;
-    op.cache_flush();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate(
-      "foo", completion, &op,
-      librados::OPERATION_IGNORE_OVERLAY, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-
-  // evict
-  {
-    ObjectReadOperation op;
-    op.cache_evict();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate(
-        "foo", completion, &op, librados::OPERATION_IGNORE_CACHE, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-
-  // verify no longer in cache tier
-  {
-    NObjectIterator it = cache_ioctx.nobjects_begin();
-    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
-  }
-  // or base tier
-  {
-    NObjectIterator it = ioctx.nobjects_begin();
-    ASSERT_TRUE(it == ioctx.nobjects_end());
-  }
-}
-
-TEST_F(LibRadosTwoPoolsPP, FlushSnap) {
-  // configure cache
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name +
-    "\", \"force_nonempty\": \"--force-nonempty\" }",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
-    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
-    "\", \"mode\": \"writeback\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  // create object
-  {
-    bufferlist bl;
-    bl.append("a");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-
-  // create a snapshot, clone
-  vector<uint64_t> my_snaps(1);
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
-                                                        my_snaps));
-  {
-    bufferlist bl;
-    bl.append("b");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-
-  // and another
-  my_snaps.resize(2);
-  my_snaps[1] = my_snaps[0];
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
-                                                        my_snaps));
-  {
-    bufferlist bl;
-    bl.append("c");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-
-  // verify the object is present in the cache tier
-  {
-    NObjectIterator it = cache_ioctx.nobjects_begin();
-    ASSERT_TRUE(it != cache_ioctx.nobjects_end());
-    ASSERT_TRUE(it->get_oid() == string("foo"));
-    ++it;
-    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
-  }
-
-  // verify the object is NOT present in the base tier
-  {
-    NObjectIterator it = ioctx.nobjects_begin();
-    ASSERT_TRUE(it == ioctx.nobjects_end());
-  }
-
-  // flush on head (should fail)
-  ioctx.snap_set_read(librados::SNAP_HEAD);
-  {
-    ObjectReadOperation op;
-    op.cache_flush();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate(
-      "foo", completion, &op,
-      librados::OPERATION_IGNORE_CACHE, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(-EBUSY, completion->get_return_value());
-    completion->release();
-  }
-  // flush on recent snap (should fail)
-  ioctx.snap_set_read(my_snaps[0]);
-  {
-    ObjectReadOperation op;
-    op.cache_flush();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate(
-      "foo", completion, &op,
-      librados::OPERATION_IGNORE_CACHE, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(-EBUSY, completion->get_return_value());
-    completion->release();
-  }
-  // flush on oldest snap
-  ioctx.snap_set_read(my_snaps[1]);
-  {
-    ObjectReadOperation op;
-    op.cache_flush();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate(
-      "foo", completion, &op,
-      librados::OPERATION_IGNORE_CACHE, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-  // flush on next oldest snap
-  ioctx.snap_set_read(my_snaps[0]);
-  {
-    ObjectReadOperation op;
-    op.cache_flush();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate(
-      "foo", completion, &op,
-      librados::OPERATION_IGNORE_CACHE, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-  // flush on head
-  ioctx.snap_set_read(librados::SNAP_HEAD);
-  {
-    ObjectReadOperation op;
-    op.cache_flush();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate(
-      "foo", completion, &op,
-      librados::OPERATION_IGNORE_CACHE, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-
-  // verify i can read the snaps from the cache pool
-  ioctx.snap_set_read(librados::SNAP_HEAD);
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
-    ASSERT_EQ('c', bl[0]);
-  }
-  ioctx.snap_set_read(my_snaps[0]);
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
-    ASSERT_EQ('b', bl[0]);
-  }
-  ioctx.snap_set_read(my_snaps[1]);
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
-    ASSERT_EQ('a', bl[0]);
-  }
-
-  // remove overlay
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier remove-overlay\", \"pool\": \"" + pool_name +
-    "\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  // verify i can read the snaps from the base pool
-  ioctx.snap_set_read(librados::SNAP_HEAD);
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
-    ASSERT_EQ('c', bl[0]);
-  }
-  ioctx.snap_set_read(my_snaps[0]);
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
-    ASSERT_EQ('b', bl[0]);
-  }
-  ioctx.snap_set_read(my_snaps[1]);
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
-    ASSERT_EQ('a', bl[0]);
-  }
-
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
-    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-
-  // cleanup
-  ioctx.selfmanaged_snap_remove(my_snaps[0]);
-}
-
-TEST_F(LibRadosTierPP, FlushWriteRaces) {
-  Rados cluster;
-  std::string pool_name = get_temp_pool_name();
-  std::string cache_pool_name = pool_name + "-cache";
-  ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
-  ASSERT_EQ(0, cluster.pool_create(cache_pool_name.c_str()));
-  IoCtx cache_ioctx;
-  ASSERT_EQ(0, cluster.ioctx_create(cache_pool_name.c_str(), cache_ioctx));
-  cache_ioctx.application_enable("rados", true);
-  IoCtx ioctx;
-  ASSERT_EQ(0, cluster.ioctx_create(pool_name.c_str(), ioctx));
-
-  // configure cache
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
-    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
-    "\", \"mode\": \"writeback\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  // create/dirty object
-  bufferlist bl;
-  bl.append("hi there");
-  {
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-
-  // flush + write
-  {
-    ObjectReadOperation op;
-    op.cache_flush();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate(
-      "foo", completion, &op,
-      librados::OPERATION_IGNORE_OVERLAY, NULL));
-
-    ObjectWriteOperation op2;
-    op2.write_full(bl);
-    librados::AioCompletion *completion2 = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate(
-      "foo", completion2, &op2, 0));
-
-    completion->wait_for_safe();
-    completion2->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    ASSERT_EQ(0, completion2->get_return_value());
-    completion->release();
-    completion2->release();
-  }
-
-  int tries = 1000;
-  do {
-    // create/dirty object
-    {
-      bufferlist bl;
-      bl.append("hi there");
-      ObjectWriteOperation op;
-      op.write_full(bl);
-      ASSERT_EQ(0, ioctx.operate("foo", &op));
-    }
-
-    // try-flush + write
-    {
-      ObjectReadOperation op;
-      op.cache_try_flush();
-      librados::AioCompletion *completion = cluster.aio_create_completion();
-      ASSERT_EQ(0, cache_ioctx.aio_operate(
-        "foo", completion, &op,
-       librados::OPERATION_IGNORE_OVERLAY |
-       librados::OPERATION_SKIPRWLOCKS, NULL));
-
-      ObjectWriteOperation op2;
-      op2.write_full(bl);
-      librados::AioCompletion *completion2 = cluster.aio_create_completion();
-      ASSERT_EQ(0, ioctx.aio_operate("foo", completion2, &op2, 0));
-
-      completion->wait_for_safe();
-      completion2->wait_for_safe();
-      int r = completion->get_return_value();
-      ASSERT_TRUE(r == -EBUSY || r == 0);
-      ASSERT_EQ(0, completion2->get_return_value());
-      completion->release();
-      completion2->release();
-      if (r == -EBUSY)
-       break;
-      cout << "didn't get EBUSY, trying again" << std::endl;
-    }
-    ASSERT_TRUE(--tries);
-  } while (true);
-
-  // tear down tiers
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier remove-overlay\", \"pool\": \"" + pool_name +
-    "\"}",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle before next test
-  cluster.wait_for_latest_osdmap();
-
-  ASSERT_EQ(0, cluster.pool_delete(cache_pool_name.c_str()));
-  ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
-}
-
-TEST_F(LibRadosTwoPoolsPP, FlushTryFlushRaces) {
-  // configure cache
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name +
-    "\", \"force_nonempty\": \"--force-nonempty\" }",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
-    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
-    "\", \"mode\": \"writeback\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  // create/dirty object
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-
-  // flush + flush
-  {
-    ObjectReadOperation op;
-    op.cache_flush();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate(
-      "foo", completion, &op,
-      librados::OPERATION_IGNORE_OVERLAY, NULL));
-
-    ObjectReadOperation op2;
-    op2.cache_flush();
-    librados::AioCompletion *completion2 = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate(
-      "foo", completion2, &op2,
-      librados::OPERATION_IGNORE_OVERLAY, NULL));
-
-    completion->wait_for_safe();
-    completion2->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    ASSERT_EQ(0, completion2->get_return_value());
-    completion->release();
-    completion2->release();
-  }
-
-  // create/dirty object
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-
-  // flush + try-flush
-  {
-    ObjectReadOperation op;
-    op.cache_flush();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate(
-      "foo", completion, &op,
-      librados::OPERATION_IGNORE_OVERLAY, NULL));
-
-    ObjectReadOperation op2;
-    op2.cache_try_flush();
-    librados::AioCompletion *completion2 = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate(
-      "foo", completion2, &op2,
-      librados::OPERATION_IGNORE_OVERLAY |
-      librados::OPERATION_SKIPRWLOCKS, NULL));
-
-    completion->wait_for_safe();
-    completion2->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    ASSERT_EQ(0, completion2->get_return_value());
-    completion->release();
-    completion2->release();
-  }
-
-  // create/dirty object
-  int tries = 1000;
-  do {
-    {
-      bufferlist bl;
-      bl.append("hi there");
-      ObjectWriteOperation op;
-      op.write_full(bl);
-      ASSERT_EQ(0, ioctx.operate("foo", &op));
-    }
-
-    // try-flush + flush
-    //  (flush will not piggyback on try-flush)
-    {
-      ObjectReadOperation op;
-      op.cache_try_flush();
-      librados::AioCompletion *completion = cluster.aio_create_completion();
-      ASSERT_EQ(0, cache_ioctx.aio_operate(
-        "foo", completion, &op,
-       librados::OPERATION_IGNORE_OVERLAY |
-       librados::OPERATION_SKIPRWLOCKS, NULL));
-
-      ObjectReadOperation op2;
-      op2.cache_flush();
-      librados::AioCompletion *completion2 = cluster.aio_create_completion();
-      ASSERT_EQ(0, cache_ioctx.aio_operate(
-        "foo", completion2, &op2,
-       librados::OPERATION_IGNORE_OVERLAY, NULL));
-
-      completion->wait_for_safe();
-      completion2->wait_for_safe();
-      int r = completion->get_return_value();
-      ASSERT_TRUE(r == -EBUSY || r == 0);
-      ASSERT_EQ(0, completion2->get_return_value());
-      completion->release();
-      completion2->release();
-      if (r == -EBUSY)
-       break;
-      cout << "didn't get EBUSY, trying again" << std::endl;
-    }
-    ASSERT_TRUE(--tries);
-  } while (true);
-
-  // create/dirty object
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-
-  // try-flush + try-flush
-  {
-    ObjectReadOperation op;
-    op.cache_try_flush();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate(
-      "foo", completion, &op,
-      librados::OPERATION_IGNORE_OVERLAY |
-      librados::OPERATION_SKIPRWLOCKS, NULL));
-
-    ObjectReadOperation op2;
-    op2.cache_try_flush();
-    librados::AioCompletion *completion2 = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate(
-      "foo", completion2, &op2,
-      librados::OPERATION_IGNORE_OVERLAY |
-      librados::OPERATION_SKIPRWLOCKS, NULL));
-
-    completion->wait_for_safe();
-    completion2->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    ASSERT_EQ(0, completion2->get_return_value());
-    completion->release();
-    completion2->release();
-  }
-}
-
-
-IoCtx *read_ioctx = 0;
-Mutex test_lock("FlushReadRaces::lock");
-Cond cond;
-int max_reads = 100;
-int num_reads = 0; // in progress
-
-void flush_read_race_cb(completion_t cb, void *arg);
-
-void start_flush_read()
-{
-  //cout << " starting read" << std::endl;
-  ObjectReadOperation op;
-  op.stat(NULL, NULL, NULL);
-  librados::AioCompletion *completion = completions.getCompletion();
-  completion->set_complete_callback(0, flush_read_race_cb);
-  read_ioctx->aio_operate("foo", completion, &op, NULL);
-}
-
-void flush_read_race_cb(completion_t cb, void *arg)
-{
-  //cout << " finished read" << std::endl;
-  test_lock.Lock();
-  if (num_reads > max_reads) {
-    num_reads--;
-    cond.Signal();
-  } else {
-    start_flush_read();
-  }
-  test_lock.Unlock();
-}
-
-TEST_F(LibRadosTwoPoolsPP, TryFlushReadRace) {
-  // configure cache
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name +
-    "\", \"force_nonempty\": \"--force-nonempty\" }",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
-    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
-    "\", \"mode\": \"writeback\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  // create/dirty object
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    bufferptr bp(4000000);  // make it big!
-    bp.zero();
-    bl.append(bp);
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-
-  // start a continuous stream of reads
-  read_ioctx = &ioctx;
-  test_lock.Lock();
-  for (int i = 0; i < max_reads; ++i) {
-    start_flush_read();
-    num_reads++;
-  }
-  test_lock.Unlock();
-
-  // try-flush
-  ObjectReadOperation op;
-  op.cache_try_flush();
-  librados::AioCompletion *completion = cluster.aio_create_completion();
-  ASSERT_EQ(0, cache_ioctx.aio_operate(
-      "foo", completion, &op,
-      librados::OPERATION_IGNORE_OVERLAY |
-      librados::OPERATION_SKIPRWLOCKS, NULL));
-
-  completion->wait_for_safe();
-  ASSERT_EQ(0, completion->get_return_value());
-  completion->release();
-
-  // stop reads
-  test_lock.Lock();
-  max_reads = 0;
-  while (num_reads > 0)
-    cond.Wait(test_lock);
-  test_lock.Unlock();
-}
-
-TEST_F(LibRadosTierPP, HitSetNone) {
-  {
-    list< pair<time_t,time_t> > ls;
-    AioCompletion *c = librados::Rados::aio_create_completion();
-    ASSERT_EQ(0, ioctx.hit_set_list(123, c, &ls));
-    c->wait_for_complete();
-    ASSERT_EQ(0, c->get_return_value());
-    ASSERT_TRUE(ls.empty());
-    c->release();
-  }
-  {
-    bufferlist bl;
-    AioCompletion *c = librados::Rados::aio_create_completion();
-    ASSERT_EQ(0, ioctx.hit_set_get(123, c, 12345, &bl));
-    c->wait_for_complete();
-    ASSERT_EQ(-ENOENT, c->get_return_value());
-    c->release();
-  }
-}
-
-string set_pool_str(string pool, string var, string val)
-{
-  return string("{\"prefix\": \"osd pool set\",\"pool\":\"") + pool
-    + string("\",\"var\": \"") + var + string("\",\"val\": \"")
-    + val + string("\"}");
-}
-
-string set_pool_str(string pool, string var, int val)
-{
-  return string("{\"prefix\": \"osd pool set\",\"pool\":\"") + pool
-    + string("\",\"var\": \"") + var + string("\",\"val\": \"")
-    + stringify(val) + string("\"}");
-}
-
-TEST_F(LibRadosTwoPoolsPP, HitSetRead) {
-  // make it a tier
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name +
-    "\", \"force_nonempty\": \"--force-nonempty\" }",
-    inbl, NULL, NULL));
-
-  // enable hitset tracking for this pool
-  ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_count", 2),
-                                               inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_period", 600),
-                                               inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_type",
-                                               "explicit_object"),
-                                  inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  cache_ioctx.set_namespace("");
-
-  // keep reading until we see our object appear in the HitSet
-  utime_t start = ceph_clock_now();
-  utime_t hard_stop = start + utime_t(600, 0);
-
-  while (true) {
-    utime_t now = ceph_clock_now();
-    ASSERT_TRUE(now < hard_stop);
-
-    string name = "foo";
-    uint32_t hash; 
-    ASSERT_EQ(0, cache_ioctx.get_object_hash_position2(name, &hash));
-    hobject_t oid(sobject_t(name, CEPH_NOSNAP), "", hash,
-                 cluster.pool_lookup(cache_pool_name.c_str()), "");
-
-    bufferlist bl;
-    ASSERT_EQ(-ENOENT, cache_ioctx.read("foo", bl, 1, 0));
-
-    bufferlist hbl;
-    AioCompletion *c = librados::Rados::aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.hit_set_get(hash, c, now.sec(), &hbl));
-    c->wait_for_complete();
-    c->release();
-
-    if (hbl.length()) {
-      auto p = hbl.cbegin();
-      HitSet hs;
-      decode(hs, p);
-      if (hs.contains(oid)) {
-       cout << "ok, hit_set contains " << oid << std::endl;
-       break;
-      }
-      cout << "hmm, not in HitSet yet" << std::endl;
-    } else {
-      cout << "hmm, no HitSet yet" << std::endl;
-    }
-
-    sleep(1);
-  }
-}
-
-static int _get_pg_num(Rados& cluster, string pool_name)
-{
-  bufferlist inbl;
-  string cmd = string("{\"prefix\": \"osd pool get\",\"pool\":\"")
-    + pool_name
-    + string("\",\"var\": \"pg_num\",\"format\": \"json\"}");
-  bufferlist outbl;
-  int r = cluster.mon_command(cmd, inbl, &outbl, NULL);
-  ceph_assert(r >= 0);
-  string outstr(outbl.c_str(), outbl.length());
-  json_spirit::Value v;
-  if (!json_spirit::read(outstr, v)) {
-    cerr <<" unable to parse json " << outstr << std::endl;
-    return -1;
-  }
-
-  json_spirit::Object& o = v.get_obj();
-  for (json_spirit::Object::size_type i=0; i<o.size(); i++) {
-    json_spirit::Pair& p = o[i];
-    if (p.name_ == "pg_num") {
-      cout << "pg_num = " << p.value_.get_int() << std::endl;
-      return p.value_.get_int();
-    }
-  }
-  cerr << "didn't find pg_num in " << outstr << std::endl;
-  return -1;
-}
-
-
-TEST_F(LibRadosTwoPoolsPP, HitSetWrite) {
-  int num_pg = _get_pg_num(cluster, pool_name);
-  ceph_assert(num_pg > 0);
-
-  // make it a tier
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name +
-    "\", \"force_nonempty\": \"--force-nonempty\" }",
-    inbl, NULL, NULL));
-
-  // enable hitset tracking for this pool
-  ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_count", 8),
-                                               inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_period", 600),
-                                               inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_type",
-                                               "explicit_hash"),
-                                  inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  cache_ioctx.set_namespace("");
-
-  int num = 200;
-
-  // do a bunch of writes
-  for (int i=0; i<num; ++i) {
-    bufferlist bl;
-    bl.append("a");
-    ASSERT_EQ(0, cache_ioctx.write(stringify(i), bl, 1, 0));
-  }
-
-  // get HitSets
-  std::map<int,HitSet> hitsets;
-  for (int i=0; i<num_pg; ++i) {
-    list< pair<time_t,time_t> > ls;
-    AioCompletion *c = librados::Rados::aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.hit_set_list(i, c, &ls));
-    c->wait_for_complete();
-    c->release();
-    std::cout << "pg " << i << " ls " << ls << std::endl;
-    ASSERT_FALSE(ls.empty());
-
-    // get the latest
-    c = librados::Rados::aio_create_completion();
-    bufferlist bl;
-    ASSERT_EQ(0, cache_ioctx.hit_set_get(i, c, ls.back().first, &bl));
-    c->wait_for_complete();
-    c->release();
-
-    try {
-      auto p = bl.cbegin();
-      decode(hitsets[i], p);
-    }
-    catch (buffer::error& e) {
-      std::cout << "failed to decode hit set; bl len is " << bl.length() << "\n";
-      bl.hexdump(std::cout);
-      std::cout << std::endl;
-      throw e;
-    }
-
-    // cope with racing splits by refreshing pg_num
-    if (i == num_pg - 1)
-      num_pg = _get_pg_num(cluster, cache_pool_name);
-  }
-
-  for (int i=0; i<num; ++i) {
-    string n = stringify(i);
-    uint32_t hash;
-    ASSERT_EQ(0, cache_ioctx.get_object_hash_position2(n, &hash));
-    hobject_t oid(sobject_t(n, CEPH_NOSNAP), "", hash,
-                 cluster.pool_lookup(cache_pool_name.c_str()), "");
-    std::cout << "checking for " << oid << std::endl;
-    bool found = false;
-    for (int p=0; p<num_pg; ++p) {
-      if (hitsets[p].contains(oid)) {
-       found = true;
-       break;
-      }
-    }
-    ASSERT_TRUE(found);
-  }
-}
-
-TEST_F(LibRadosTwoPoolsPP, HitSetTrim) {
-  unsigned count = 3;
-  unsigned period = 3;
-
-  // make it a tier
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name +
-    "\", \"force_nonempty\": \"--force-nonempty\" }",
-    inbl, NULL, NULL));
-
-  // enable hitset tracking for this pool
-  ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_count", count),
-                                               inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_period", period),
-                                               inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_type", "bloom"),
-                                  inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_fpp", ".01"),
-                                  inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  cache_ioctx.set_namespace("");
-
-  // do a bunch of writes and make sure the hitsets rotate
-  utime_t start = ceph_clock_now();
-  utime_t hard_stop = start + utime_t(count * period * 50, 0);
-
-  time_t first = 0;
-  while (true) {
-    string name = "foo";
-    uint32_t hash; 
-    ASSERT_EQ(0, cache_ioctx.get_object_hash_position2(name, &hash));
-    hobject_t oid(sobject_t(name, CEPH_NOSNAP), "", hash, -1, "");
-
-    bufferlist bl;
-    bl.append("f");
-    ASSERT_EQ(0, cache_ioctx.write("foo", bl, 1, 0));
-
-    list<pair<time_t, time_t> > ls;
-    AioCompletion *c = librados::Rados::aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.hit_set_list(hash, c, &ls));
-    c->wait_for_complete();
-    c->release();
-
-    cout << " got ls " << ls << std::endl;
-    if (!ls.empty()) {
-      if (!first) {
-       first = ls.front().first;
-       cout << "first is " << first << std::endl;
-      } else {
-       if (ls.front().first != first) {
-         cout << "first now " << ls.front().first << ", trimmed" << std::endl;
-         break;
-       }
-      }
-    }
-
-    utime_t now = ceph_clock_now();
-    ASSERT_TRUE(now < hard_stop);
-
-    sleep(1);
-  }
-}
-
-TEST_F(LibRadosTwoPoolsPP, PromoteOn2ndRead) {
-  // create object
-  for (int i=0; i<20; ++i) {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo" + stringify(i), &op));
-  }
-
-  // configure cache
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name +
-    "\", \"force_nonempty\": \"--force-nonempty\" }",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
-    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
-    "\", \"mode\": \"writeback\"}",
-    inbl, NULL, NULL));
-
-  // enable hitset tracking for this pool
-  ASSERT_EQ(0, cluster.mon_command(
-    set_pool_str(cache_pool_name, "hit_set_count", 2),
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    set_pool_str(cache_pool_name, "hit_set_period", 600),
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    set_pool_str(cache_pool_name, "hit_set_type", "bloom"),
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    set_pool_str(cache_pool_name, "min_read_recency_for_promote", 1),
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    set_pool_str(cache_pool_name, "hit_set_grade_decay_rate", 20),
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    set_pool_str(cache_pool_name, "hit_set_search_last_n", 1),
-    inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  int fake = 0;  // set this to non-zero to test spurious promotion,
-                // e.g. from thrashing
-  int attempt = 0;
-  string obj;
-  while (true) {
-    // 1st read, don't trigger a promote
-    obj = "foo" + stringify(attempt);
-    cout << obj << std::endl;
-    {
-      bufferlist bl;
-      ASSERT_EQ(1, ioctx.read(obj.c_str(), bl, 1, 0));
-      if (--fake >= 0) {
-       sleep(1);
-       ASSERT_EQ(1, ioctx.read(obj.c_str(), bl, 1, 0));
-       sleep(1);
-      }
-    }
-
-    // verify the object is NOT present in the cache tier
-    {
-      bool found = false;
-      NObjectIterator it = cache_ioctx.nobjects_begin();
-      while (it != cache_ioctx.nobjects_end()) {
-       cout << " see " << it->get_oid() << std::endl;
-       if (it->get_oid() == string(obj.c_str())) {
-         found = true;
-         break;
-       }
-       ++it;
-      }
-      if (!found)
-       break;
-    }
-
-    ++attempt;
-    ASSERT_LE(attempt, 20);
-    cout << "hrm, object is present in cache on attempt " << attempt
-        << ", retrying" << std::endl;
-  }
-
-  // Read until the object is present in the cache tier
-  cout << "verifying " << obj << " is eventually promoted" << std::endl;
-  while (true) {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read(obj.c_str(), bl, 1, 0));
-
-    bool there = false;
-    NObjectIterator it = cache_ioctx.nobjects_begin();
-    while (it != cache_ioctx.nobjects_end()) {
-      if (it->get_oid() == string(obj.c_str())) {
-       there = true;
-       break;
-      }
-      ++it;
-    }
-    if (there)
-      break;
-
-    sleep(1);
-  }
-
-  // tear down tiers
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier remove-overlay\", \"pool\": \"" + pool_name +
-    "\"}",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle before next test
-  cluster.wait_for_latest_osdmap();
-}
-
-TEST_F(LibRadosTwoPoolsPP, ProxyRead) {
-  // create object
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-
-  // configure cache
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name +
-    "\", \"force_nonempty\": \"--force-nonempty\" }",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
-    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
-    "\", \"mode\": \"readproxy\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  // read and verify the object
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
-    ASSERT_EQ('h', bl[0]);
-  }
-
-  // Verify 10 times the object is NOT present in the cache tier
-  uint32_t i = 0;
-  while (i++ < 10) {
-    NObjectIterator it = cache_ioctx.nobjects_begin();
-    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
-    sleep(1);
-  }
-
-  // tear down tiers
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier remove-overlay\", \"pool\": \"" + pool_name +
-    "\"}",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle before next test
-  cluster.wait_for_latest_osdmap();
-}
-
-TEST_F(LibRadosTwoPoolsPP, CachePin) {
-  // create object
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("bar", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("baz", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("bam", &op));
-  }
-
-  // configure cache
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name +
-    "\", \"force_nonempty\": \"--force-nonempty\" }",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
-    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
-    "\", \"mode\": \"writeback\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  // read, trigger promote
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
-    ASSERT_EQ(1, ioctx.read("bar", bl, 1, 0));
-    ASSERT_EQ(1, ioctx.read("baz", bl, 1, 0));
-    ASSERT_EQ(1, ioctx.read("bam", bl, 1, 0));
-  }
-
-  // verify the objects are present in the cache tier
-  {
-    NObjectIterator it = cache_ioctx.nobjects_begin();
-    ASSERT_TRUE(it != cache_ioctx.nobjects_end());
-    for (uint32_t i = 0; i < 4; i++) {
-      ASSERT_TRUE(it->get_oid() == string("foo") ||
-                  it->get_oid() == string("bar") ||
-                  it->get_oid() == string("baz") ||
-                  it->get_oid() == string("bam"));
-      ++it;
-    }
-    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
-  }
-
-  // pin objects
-  {
-    ObjectWriteOperation op;
-    op.cache_pin();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-  {
-    ObjectWriteOperation op;
-    op.cache_pin();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate("baz", completion, &op));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-
-  // enable agent
-  ASSERT_EQ(0, cluster.mon_command(
-    set_pool_str(cache_pool_name, "hit_set_count", 2),
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    set_pool_str(cache_pool_name, "hit_set_period", 600),
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    set_pool_str(cache_pool_name, "hit_set_type", "bloom"),
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    set_pool_str(cache_pool_name, "min_read_recency_for_promote", 1),
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    set_pool_str(cache_pool_name, "target_max_objects", 1),
-    inbl, NULL, NULL));
-
-  sleep(10);
-
-  // Verify the pinned object 'foo' is not flushed/evicted
-  uint32_t count = 0;
-  while (true) {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("baz", bl, 1, 0));
-
-    count = 0;
-    NObjectIterator it = cache_ioctx.nobjects_begin();
-    while (it != cache_ioctx.nobjects_end()) {
-      ASSERT_TRUE(it->get_oid() == string("foo") ||
-                  it->get_oid() == string("bar") ||
-                  it->get_oid() == string("baz") ||
-                  it->get_oid() == string("bam"));
-      ++count;
-      ++it;
-    }
-    if (count == 2) {
-      ASSERT_TRUE(it->get_oid() == string("foo") ||
-                  it->get_oid() == string("baz"));
-      break;
-    }
-
-    sleep(1);
-  }
-
-  // tear down tiers
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier remove-overlay\", \"pool\": \"" + pool_name +
-    "\"}",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle before next test
-  cluster.wait_for_latest_osdmap();
-}
-
-TEST_F(LibRadosTwoPoolsPP, SetRedirectRead) {
-  // skip test if not yet luminous
-  {
-    bufferlist inbl, outbl;
-    ASSERT_EQ(0, cluster.mon_command(
-               "{\"prefix\": \"osd dump\"}",
-               inbl, &outbl, NULL));
-    string s(outbl.c_str(), outbl.length());
-    if (s.find("luminous") == std::string::npos) {
-      cout << "cluster is not yet luminous, skipping test" << std::endl;
-      return;
-    }
-  }
-
-  // create object
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, cache_ioctx.operate("bar", &op));
-  }
-
-  // configure tier
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name +
-    "\", \"force_nonempty\": \"--force-nonempty\" }",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  {
-    ObjectWriteOperation op;
-    op.set_redirect("bar", cache_ioctx, 0);
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate("foo", completion, &op));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-  // read and verify the object
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
-    ASSERT_EQ('t', bl[0]);
-  }
-
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle before next test
-  cluster.wait_for_latest_osdmap();
-}
-
-TEST_F(LibRadosTwoPoolsPP, SetChunkRead) {
-  // skip test if not yet mimic
-  {
-    bufferlist inbl, outbl;
-    ASSERT_EQ(0, cluster.mon_command(
-               "{\"prefix\": \"osd dump\"}",
-               inbl, &outbl, NULL));
-    string s(outbl.c_str(), outbl.length());
-    if (s.find("mimic") == std::string::npos) {
-      cout << "cluster is not yet mimic, skipping test" << std::endl;
-      return;
-    }
-  }
-
-  // create object
-  {
-    ObjectWriteOperation op;
-    op.create(true);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, cache_ioctx.operate("bar", &op));
-  }
-
-  // configure tier
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name +
-    "\", \"force_nonempty\": \"--force-nonempty\" }",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  // set_chunk
-  {
-    ObjectWriteOperation op;
-    int len = strlen("hi there");
-    for (int i = 0; i < len; i+=2) {
-      op.set_chunk(i, 2, cache_ioctx, "bar", i);
-    }
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate("foo", completion, &op));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-
-  // make all chunks dirty --> full flush --> all chunks are evicted
-  {
-    bufferlist bl;
-    bl.append("There hi");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-
-  // read and verify the object
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
-    ASSERT_EQ('T', bl[0]);
-  }
-
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle before next test
-  cluster.wait_for_latest_osdmap();
-}
-
-TEST_F(LibRadosTwoPoolsPP, ManifestPromoteRead) {
-  // skip test if not yet mimic
-  {
-    bufferlist inbl, outbl;
-    ASSERT_EQ(0, cluster.mon_command(
-               "{\"prefix\": \"osd dump\"}",
-               inbl, &outbl, NULL));
-    string s(outbl.c_str(), outbl.length());
-    if (s.find("mimic") == std::string::npos) {
-      cout << "cluster is not yet mimic, skipping test" << std::endl;
-      return;
-    }
-  }
-
-  // create object
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("base chunk");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo-chunk", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, cache_ioctx.operate("bar", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("CHUNK");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, cache_ioctx.operate("bar-chunk", &op));
-  }
-
-  // configure tier
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name +
-    "\", \"force_nonempty\": \"--force-nonempty\" }",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  // set-redirect
-  {
-    ObjectWriteOperation op;
-    op.set_redirect("bar", cache_ioctx, 0);
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate("foo", completion, &op));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-  // set-chunk
-  {
-    ObjectWriteOperation op;
-    op.set_chunk(0, 2, cache_ioctx, "bar-chunk", 0);
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate("foo-chunk", completion, &op));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-  // promote
-  {
-    ObjectWriteOperation op;
-    op.tier_promote();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate("foo", completion, &op));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-  // read and verify the object (redirect)
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
-    ASSERT_EQ('t', bl[0]);
-  }
-  // promote
-  {
-    ObjectWriteOperation op;
-    op.tier_promote();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate("foo-chunk", completion, &op));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-  // read and verify the object
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("foo-chunk", bl, 1, 0));
-    ASSERT_EQ('C', bl[0]);
-  }
-
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle before next test
-  cluster.wait_for_latest_osdmap();
-}
-
-TEST_F(LibRadosTwoPoolsPP, ManifestRefRead) {
-  // skip test if not yet mimic
-  {
-    bufferlist inbl, outbl;
-    ASSERT_EQ(0, cluster.mon_command(
-               "{\"prefix\": \"osd dump\"}",
-               inbl, &outbl, NULL));
-    string s(outbl.c_str(), outbl.length());
-    if (s.find("mimic") == std::string::npos) {
-      cout << "cluster is not yet mimic, skipping test" << std::endl;
-      return;
-    }
-  }
-
-  // create object
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("base chunk");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo-chunk", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, cache_ioctx.operate("bar", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("CHUNK");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, cache_ioctx.operate("bar-chunk", &op));
-  }
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  // set-redirect
-  {
-    ObjectWriteOperation op;
-    op.set_redirect("bar", cache_ioctx, 0, CEPH_OSD_OP_FLAG_WITH_REFERENCE);
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate("foo", completion, &op));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-  // set-chunk
-  {
-    ObjectWriteOperation op;
-    op.set_chunk(0, 2, cache_ioctx, "bar-chunk", 0, CEPH_OSD_OP_FLAG_WITH_REFERENCE);
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate("foo-chunk", completion, &op));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-  // redirect's refcount 
-  {
-    bufferlist in, out;
-    cache_ioctx.exec("bar", "cas", "chunk_read", in, out);
-    cls_chunk_refcount_read_ret read_ret;
-    try {
-      auto iter = out.cbegin();
-      decode(read_ret, iter);
-    } catch (buffer::error& err) {
-      ASSERT_TRUE(0);
-    }
-    ASSERT_EQ(1U, read_ret.refs.size());
-  }
-  // chunk's refcount 
-  {
-    bufferlist in, out;
-    cache_ioctx.exec("bar-chunk", "cas", "chunk_read", in, out);
-    cls_chunk_refcount_read_ret read_ret;
-    try {
-      auto iter = out.cbegin();
-      decode(read_ret, iter);
-    } catch (buffer::error& err) {
-      ASSERT_TRUE(0);
-    }
-    ASSERT_EQ(1u, read_ret.refs.size());
-  }
-
-  // wait for maps to settle before next test
-  cluster.wait_for_latest_osdmap();
-}
-
-TEST_F(LibRadosTwoPoolsPP, ManifestUnset) {
-  // skip test if not yet nautilus
-  {
-    bufferlist inbl, outbl;
-    ASSERT_EQ(0, cluster.mon_command(
-               "{\"prefix\": \"osd dump\"}",
-               inbl, &outbl, NULL));
-    string s(outbl.c_str(), outbl.length());
-    if (s.find("nautilus") == std::string::npos) {
-      cout << "cluster is not yet nautilus, skipping test" << std::endl;
-      return;
-    }
-  }
-
-  // create object
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("base chunk");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo-chunk", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, cache_ioctx.operate("bar", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("CHUNK");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, cache_ioctx.operate("bar-chunk", &op));
-  }
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  // set-redirect
-  {
-    ObjectWriteOperation op;
-    op.set_redirect("bar", cache_ioctx, 0, CEPH_OSD_OP_FLAG_WITH_REFERENCE);
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate("foo", completion, &op));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-  // set-chunk
-  {
-    ObjectWriteOperation op;
-    op.set_chunk(0, 2, cache_ioctx, "bar-chunk", 0, CEPH_OSD_OP_FLAG_WITH_REFERENCE);
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate("foo-chunk", completion, &op));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-  // redirect's refcount 
-  {
-    bufferlist in, out;
-    cache_ioctx.exec("bar", "cas", "chunk_read", in, out);
-    cls_chunk_refcount_read_ret read_ret;
-    try {
-      auto iter = out.cbegin();
-      decode(read_ret, iter);
-    } catch (buffer::error& err) {
-      ASSERT_TRUE(0);
-    }
-    ASSERT_EQ(1u, read_ret.refs.size());
-  }
-  // chunk's refcount 
-  {
-    bufferlist in, out;
-    cache_ioctx.exec("bar-chunk", "cas", "chunk_read", in, out);
-    cls_chunk_refcount_read_ret read_ret;
-    try {
-      auto iter = out.cbegin();
-      decode(read_ret, iter);
-    } catch (buffer::error& err) {
-      ASSERT_TRUE(0);
-    }
-    ASSERT_EQ(1u, read_ret.refs.size());
-  }
-
-  // unset-manifest for set-redirect
-  {
-    ObjectWriteOperation op;
-    op.unset_manifest();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate("foo", completion, &op));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-
-  // unset-manifest for set-chunk
-  {
-    ObjectWriteOperation op;
-    op.unset_manifest();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate("foo-chunk", completion, &op));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-  // redirect's refcount 
-  {
-    bufferlist in, out;
-    cache_ioctx.exec("bar", "cas", "chunk_read", in, out);
-    if (out.length() != 0U) {
-      ObjectWriteOperation op;
-      op.unset_manifest();
-      librados::AioCompletion *completion = cluster.aio_create_completion();
-      ASSERT_EQ(0, ioctx.aio_operate("foo", completion, &op));
-      completion->wait_for_safe();
-      ASSERT_EQ(-EOPNOTSUPP, completion->get_return_value());
-      completion->release();
-    }
-  }
-  // chunk's refcount 
-  {
-    bufferlist in, out;
-    cache_ioctx.exec("bar-chunk", "cas", "chunk_read", in, out);
-    if (out.length() != 0U) {
-      ObjectWriteOperation op;
-      op.unset_manifest();
-      librados::AioCompletion *completion = cluster.aio_create_completion();
-      ASSERT_EQ(0, ioctx.aio_operate("foo-chunk", completion, &op));
-      completion->wait_for_safe();
-      ASSERT_EQ(-EOPNOTSUPP, completion->get_return_value());
-      completion->release();
-    }
-  }
-
-  // wait for maps to settle before next test
-  cluster.wait_for_latest_osdmap();
-}
-
-#include "common/ceph_crypto.h"
-using ceph::crypto::SHA1;
-#include "rgw/rgw_common.h"
-TEST_F(LibRadosTwoPoolsPP, ManifestDedupRefRead) {
-  // skip test if not yet nautilus
-  {
-    bufferlist inbl, outbl;
-    ASSERT_EQ(0, cluster.mon_command(
-               "{\"prefix\": \"osd dump\"}",
-               inbl, &outbl, NULL));
-    string s(outbl.c_str(), outbl.length());
-    if (s.find("nautilus") == std::string::npos) {
-      cout << "cluster is not yet nautilus, skipping test" << std::endl;
-      return;
-    }
-  }
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-           set_pool_str(pool_name, "fingerprint_algorithm", "sha1"),
-           inbl, NULL, NULL));
-  cluster.wait_for_latest_osdmap();
-
-  // create object
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo-dedup", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, cache_ioctx.operate("bar", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("CHUNK");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, cache_ioctx.operate("bar-chunk", &op));
-  }
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  // set-chunk (dedup)
-  {
-    ObjectWriteOperation op;
-    int len = strlen("hi there");
-    op.set_chunk(0, len, cache_ioctx, "bar-chunk", 0, 
-               CEPH_OSD_OP_FLAG_WITH_REFERENCE);
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate("foo-dedup", completion, &op));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-  // set-chunk (dedup)
-  {
-    ObjectWriteOperation op;
-    int len = strlen("hi there");
-    op.set_chunk(0, len, cache_ioctx, "bar", 0, 
-               CEPH_OSD_OP_FLAG_WITH_REFERENCE);
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate("foo", completion, &op));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-  // make all chunks dirty --> flush 
-  {
-    // make a dirty chunks
-    bufferlist bl;
-    bl.append("There hi");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo-dedup", &op));
-  }
-  {
-    // do flush
-    bufferlist bl;
-    bl.append("There hi");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo-dedup", &op));
-  }
-  {
-    // make a dirty chunks
-    bufferlist bl;
-    bl.append("There hi");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-  {
-    // do flush
-    bufferlist bl;
-    bl.append("There hi");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-  // chunk's refcount 
-  {
-    bufferlist in, out;
-    SHA1 sha1_gen;
-    int size = strlen("There hi");
-    unsigned char fingerprint[CEPH_CRYPTO_SHA1_DIGESTSIZE + 1];
-    char p_str[CEPH_CRYPTO_SHA1_DIGESTSIZE*2+1] = {0};
-    sha1_gen.Update((const unsigned char *)"There hi", size);
-    sha1_gen.Final(fingerprint);
-    buf_to_hex(fingerprint, CEPH_CRYPTO_SHA1_DIGESTSIZE, p_str);
-    cache_ioctx.exec(p_str, "cas", "chunk_read", in, out);
-    cls_chunk_refcount_read_ret read_ret;
-    try {
-      auto iter = out.cbegin();
-      decode(read_ret, iter);
-    } catch (buffer::error& err) {
-      ASSERT_TRUE(0);
-    }
-    ASSERT_EQ(2u, read_ret.refs.size());
-  }
-
-  // wait for maps to settle before next test
-  cluster.wait_for_latest_osdmap();
-}
-
-class LibRadosTwoPoolsECPP : public RadosTestECPP
-{
-public:
-  LibRadosTwoPoolsECPP() {};
-  ~LibRadosTwoPoolsECPP() override {};
-protected:
-  static void SetUpTestCase() {
-    pool_name = get_temp_pool_name();
-    ASSERT_EQ("", create_one_ec_pool_pp(pool_name, s_cluster));
-  }
-  static void TearDownTestCase() {
-    ASSERT_EQ(0, destroy_one_ec_pool_pp(pool_name, s_cluster));
-  }
-  static std::string cache_pool_name;
-
-  void SetUp() override {
-    cache_pool_name = get_temp_pool_name();
-    ASSERT_EQ(0, s_cluster.pool_create(cache_pool_name.c_str()));
-    RadosTestECPP::SetUp();
-
-    ASSERT_EQ(0, cluster.ioctx_create(cache_pool_name.c_str(), cache_ioctx));
-    cache_ioctx.application_enable("rados", true);
-    cache_ioctx.set_namespace(nspace);
-  }
-  void TearDown() override {
-    // flush + evict cache
-    flush_evict_all(cluster, cache_ioctx);
-
-    bufferlist inbl;
-    // tear down tiers
-    ASSERT_EQ(0, cluster.mon_command(
-      "{\"prefix\": \"osd tier remove-overlay\", \"pool\": \"" + pool_name +
-      "\"}",
-      inbl, NULL, NULL));
-    ASSERT_EQ(0, cluster.mon_command(
-      "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
-      "\", \"tierpool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-
-    // wait for maps to settle before next test
-    cluster.wait_for_latest_osdmap();
-
-    RadosTestECPP::TearDown();
-
-    cleanup_default_namespace(cache_ioctx);
-    cleanup_namespace(cache_ioctx, nspace);
-
-    cache_ioctx.close();
-    ASSERT_EQ(0, s_cluster.pool_delete(cache_pool_name.c_str()));
-  }
-
-  librados::IoCtx cache_ioctx;
-};
-
-std::string LibRadosTwoPoolsECPP::cache_pool_name;
-
-TEST_F(LibRadosTierECPP, Dirty) {
-  {
-    ObjectWriteOperation op;
-    op.undirty();
-    ASSERT_EQ(0, ioctx.operate("foo", &op)); // still get 0 if it dne
-  }
-  {
-    ObjectWriteOperation op;
-    op.create(true);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-  {
-    bool dirty = false;
-    int r = -1;
-    ObjectReadOperation op;
-    op.is_dirty(&dirty, &r);
-    ASSERT_EQ(0, ioctx.operate("foo", &op, NULL));
-    ASSERT_TRUE(dirty);
-    ASSERT_EQ(0, r);
-  }
-  {
-    ObjectWriteOperation op;
-    op.undirty();
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-  {
-    ObjectWriteOperation op;
-    op.undirty();
-    ASSERT_EQ(0, ioctx.operate("foo", &op));  // still 0 if already clean
-  }
-  {
-    bool dirty = false;
-    int r = -1;
-    ObjectReadOperation op;
-    op.is_dirty(&dirty, &r);
-    ASSERT_EQ(0, ioctx.operate("foo", &op, NULL));
-    ASSERT_FALSE(dirty);
-    ASSERT_EQ(0, r);
-  }
-  //{
-  //  ObjectWriteOperation op;
-  //  op.truncate(0);  // still a write even tho it is a no-op
-  //  ASSERT_EQ(0, ioctx.operate("foo", &op));
-  //}
-  //{
-  //  bool dirty = false;
-  //  int r = -1;
-  //  ObjectReadOperation op;
-  //  op.is_dirty(&dirty, &r);
-  //  ASSERT_EQ(0, ioctx.operate("foo", &op, NULL));
-  //  ASSERT_TRUE(dirty);
-  //  ASSERT_EQ(0, r);
-  //}
-}
-
-TEST_F(LibRadosTwoPoolsECPP, Overlay) {
-  // create objects
-  {
-    bufferlist bl;
-    bl.append("base");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("cache");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, cache_ioctx.operate("foo", &op));
-  }
-
-  // configure cache
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name +
-    "\", \"force_nonempty\": \"--force-nonempty\" }",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
-    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  // by default, the overlay sends us to cache pool
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
-    ASSERT_EQ('c', bl[0]);
-  }
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, cache_ioctx.read("foo", bl, 1, 0));
-    ASSERT_EQ('c', bl[0]);
-  }
-
-  // unless we say otherwise
-  {
-    bufferlist bl;
-    ObjectReadOperation op;
-    op.read(0, 1, &bl, NULL);
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate(
-       "foo", completion, &op,
-       librados::OPERATION_IGNORE_OVERLAY, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-    ASSERT_EQ('b', bl[0]);
-  }
-}
-
-TEST_F(LibRadosTwoPoolsECPP, Promote) {
-  // create object
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-
-  // configure cache
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name +
-    "\", \"force_nonempty\": \"--force-nonempty\" }",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
-    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
-    "\", \"mode\": \"writeback\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  // read, trigger a promote
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
-  }
-
-  // read, trigger a whiteout
-  {
-    bufferlist bl;
-    ASSERT_EQ(-ENOENT, ioctx.read("bar", bl, 1, 0));
-    ASSERT_EQ(-ENOENT, ioctx.read("bar", bl, 1, 0));
-  }
-
-  // verify the object is present in the cache tier
-  {
-    NObjectIterator it = cache_ioctx.nobjects_begin();
-    ASSERT_TRUE(it != cache_ioctx.nobjects_end());
-    ASSERT_TRUE(it->get_oid() == string("foo") || it->get_oid() == string("bar"));
-    ++it;
-    ASSERT_TRUE(it->get_oid() == string("foo") || it->get_oid() == string("bar"));
-    ++it;
-    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
-  }
-}
-
-TEST_F(LibRadosTwoPoolsECPP, PromoteSnap) {
-  // create object
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("bar", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("baz", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("bam", &op));
-  }
-
-  // create a snapshot, clone
-  vector<uint64_t> my_snaps(1);
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
-                                                        my_snaps));
-  {
-    bufferlist bl;
-    bl.append("ciao!");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("ciao!");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("bar", &op));
-  }
-  {
-    ObjectWriteOperation op;
-    op.remove();
-    ASSERT_EQ(0, ioctx.operate("baz", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("ciao!");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("bam", &op));
-  }
-
-  // configure cache
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name +
-    "\", \"force_nonempty\": \"--force-nonempty\" }",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
-    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
-    "\", \"mode\": \"writeback\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  // read, trigger a promote on the head
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
-    ASSERT_EQ('c', bl[0]);
-  }
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("bam", bl, 1, 0));
-    ASSERT_EQ('c', bl[0]);
-  }
-
-  ioctx.snap_set_read(my_snaps[0]);
-
-  // stop and scrub this pg (to make sure scrub can handle missing
-  // clones in the cache tier)
-  // This test requires cache tier and base tier to have the same pg_num/pgp_num
-  {
-    for (int tries = 0; tries < 5; ++tries) {
-      IoCtx cache_ioctx;
-      ASSERT_EQ(0, cluster.ioctx_create(cache_pool_name.c_str(), cache_ioctx));
-      uint32_t hash;
-      ASSERT_EQ(0, ioctx.get_object_pg_hash_position2("foo", &hash));
-      ostringstream ss;
-      ss << "{\"prefix\": \"pg scrub\", \"pgid\": \""
-        << cache_ioctx.get_id() << "."
-        << hash
-        << "\"}";
-      int r = cluster.mon_command(ss.str(), inbl, NULL, NULL);
-      if (r == -EAGAIN ||
-         r == -ENOENT) {  // in case mgr osdmap is a bit stale
-       sleep(5);
-       continue;
-      }
-      ASSERT_EQ(0, r);
-      break;
-    }
-    // give it a few seconds to go.  this is sloppy but is usually enough time
-    cout << "waiting for scrub..." << std::endl;
-    sleep(15);
-    cout << "done waiting" << std::endl;
-  }
-
-  // read foo snap
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
-    ASSERT_EQ('h', bl[0]);
-  }
-
-  // read bar snap
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("bar", bl, 1, 0));
-    ASSERT_EQ('h', bl[0]);
-  }
-
-  // read baz snap
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("baz", bl, 1, 0));
-    ASSERT_EQ('h', bl[0]);
-  }
-
-  ioctx.snap_set_read(librados::SNAP_HEAD);
-
-  // read foo
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
-    ASSERT_EQ('c', bl[0]);
-  }
-
-  // read bar
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("bar", bl, 1, 0));
-    ASSERT_EQ('c', bl[0]);
-  }
-
-  // read baz
-  {
-    bufferlist bl;
-    ASSERT_EQ(-ENOENT, ioctx.read("baz", bl, 1, 0));
-  }
-
-  // cleanup
-  ioctx.selfmanaged_snap_remove(my_snaps[0]);
-}
-
-TEST_F(LibRadosTwoPoolsECPP, PromoteSnapTrimRace) {
-  // create object
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-
-  // create a snapshot, clone
-  vector<uint64_t> my_snaps(1);
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
-                                                        my_snaps));
-  {
-    bufferlist bl;
-    bl.append("ciao!");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-
-  // configure cache
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name +
-    "\", \"force_nonempty\": \"--force-nonempty\" }",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
-    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
-    "\", \"mode\": \"writeback\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  // delete the snap
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_remove(my_snaps[0]));
-
-  ioctx.snap_set_read(my_snaps[0]);
-
-  // read foo snap
-  {
-    bufferlist bl;
-    ASSERT_EQ(-ENOENT, ioctx.read("foo", bl, 1, 0));
-  }
-
-  // cleanup
-  ioctx.selfmanaged_snap_remove(my_snaps[0]);
-}
-
-TEST_F(LibRadosTwoPoolsECPP, Whiteout) {
-  // create object
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-
-  // configure cache
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name +
-    "\", \"force_nonempty\": \"--force-nonempty\" }",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
-    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
-    "\", \"mode\": \"writeback\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  // create some whiteouts, verify they behave
-  {
-    ObjectWriteOperation op;
-    op.assert_exists();
-    op.remove();
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-
-  {
-    ObjectWriteOperation op;
-    op.assert_exists();
-    op.remove();
-    ASSERT_EQ(-ENOENT, ioctx.operate("bar", &op));
-  }
-  {
-    ObjectWriteOperation op;
-    op.assert_exists();
-    op.remove();
-    ASSERT_EQ(-ENOENT, ioctx.operate("bar", &op));
-  }
-
-  // verify the whiteouts are there in the cache tier
-  {
-    NObjectIterator it = cache_ioctx.nobjects_begin();
-    ASSERT_TRUE(it != cache_ioctx.nobjects_end());
-    ASSERT_TRUE(it->get_oid() == string("foo") || it->get_oid() == string("bar"));
-    ++it;
-    ASSERT_TRUE(it->get_oid() == string("foo") || it->get_oid() == string("bar"));
-    ++it;
-    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
-  }
-
-  // delete a whiteout and verify it goes away
-  ASSERT_EQ(-ENOENT, ioctx.remove("foo"));
-  {
-    ObjectWriteOperation op;
-    op.remove();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate("bar", completion, &op,
-                                  librados::OPERATION_IGNORE_CACHE));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-
-    NObjectIterator it = cache_ioctx.nobjects_begin();
-    ASSERT_TRUE(it != cache_ioctx.nobjects_end());
-    ASSERT_TRUE(it->get_oid() == string("foo"));
-    ++it;
-    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
-  }
-
-  // recreate an object and verify we can read it
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
-    ASSERT_EQ('h', bl[0]);
-  }
-}
-
-TEST_F(LibRadosTwoPoolsECPP, Evict) {
-  // create object
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-
-  // configure cache
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name +
-    "\", \"force_nonempty\": \"--force-nonempty\" }",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
-    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
-    "\", \"mode\": \"writeback\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  // read, trigger a promote
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
-  }
-
-  // read, trigger a whiteout, and a dirty object
-  {
-    bufferlist bl;
-    ASSERT_EQ(-ENOENT, ioctx.read("bar", bl, 1, 0));
-    ASSERT_EQ(-ENOENT, ioctx.read("bar", bl, 1, 0));
-    ASSERT_EQ(0, ioctx.write("bar", bl, bl.length(), 0));
-  }
-
-  // verify the object is present in the cache tier
-  {
-    NObjectIterator it = cache_ioctx.nobjects_begin();
-    ASSERT_TRUE(it != cache_ioctx.nobjects_end());
-    ASSERT_TRUE(it->get_oid() == string("foo") || it->get_oid() == string("bar"));
-    ++it;
-    ASSERT_TRUE(it->get_oid() == string("foo") || it->get_oid() == string("bar"));
-    ++it;
-    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
-  }
-
-  // pin
-  {
-    ObjectWriteOperation op;
-    op.cache_pin();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-
-  // evict the pinned object with -EPERM
-  {
-    ObjectReadOperation op;
-    op.cache_evict();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op,
-                                        librados::OPERATION_IGNORE_CACHE,
-                                        NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(-EPERM, completion->get_return_value());
-    completion->release();
-  }
-
-  // unpin
-  {
-    ObjectWriteOperation op;
-    op.cache_unpin();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-
-  // flush
-  {
-    ObjectReadOperation op;
-    op.cache_flush();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate(
-      "foo", completion, &op,
-      librados::OPERATION_IGNORE_OVERLAY, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-
-  // verify clean
-  {
-    bool dirty = false;
-    int r = -1;
-    ObjectReadOperation op;
-    op.is_dirty(&dirty, &r);
-    ASSERT_EQ(0, cache_ioctx.operate("foo", &op, NULL));
-    ASSERT_FALSE(dirty);
-    ASSERT_EQ(0, r);
-  }
-
-  // evict
-  {
-    ObjectReadOperation op;
-    op.cache_evict();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op,
-                                        librados::OPERATION_IGNORE_CACHE,
-                                        NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-  {
-    ObjectReadOperation op;
-    op.cache_evict();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate(
-      "foo", completion, &op,
-      librados::OPERATION_IGNORE_CACHE, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-  {
-    ObjectReadOperation op;
-    op.cache_evict();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate(
-      "bar", completion, &op,
-      librados::OPERATION_IGNORE_CACHE, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(-EBUSY, completion->get_return_value());
-    completion->release();
-  }
-}
-
-TEST_F(LibRadosTwoPoolsECPP, EvictSnap) {
-  // create object
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("bar", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("baz", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("bam", &op));
-  }
-
-  // create a snapshot, clone
-  vector<uint64_t> my_snaps(1);
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
-                                                        my_snaps));
-  {
-    bufferlist bl;
-    bl.append("ciao!");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("ciao!");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("bar", &op));
-  }
-  {
-    ObjectWriteOperation op;
-    op.remove();
-    ASSERT_EQ(0, ioctx.operate("baz", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("ciao!");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("bam", &op));
-  }
-
-  // configure cache
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name +
-    "\", \"force_nonempty\": \"--force-nonempty\" }",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
-    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
-    "\", \"mode\": \"writeback\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  // read, trigger a promote on the head
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
-    ASSERT_EQ('c', bl[0]);
-  }
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("bam", bl, 1, 0));
-    ASSERT_EQ('c', bl[0]);
-  }
-
-  // evict bam
-  {
-    ObjectReadOperation op;
-    op.cache_evict();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate(
-      "bam", completion, &op,
-      librados::OPERATION_IGNORE_CACHE, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-  {
-    bufferlist bl;
-    ObjectReadOperation op;
-    op.read(1, 0, &bl, NULL);
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate(
-      "bam", completion, &op,
-      librados::OPERATION_IGNORE_CACHE, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(-ENOENT, completion->get_return_value());
-    completion->release();
-  }
-
-  // read foo snap
-  ioctx.snap_set_read(my_snaps[0]);
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
-    ASSERT_EQ('h', bl[0]);
-  }
-
-  // evict foo snap
-  {
-    ObjectReadOperation op;
-    op.cache_evict();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate(
-      "foo", completion, &op,
-      librados::OPERATION_IGNORE_CACHE, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-  // snap is gone...
-  {
-    bufferlist bl;
-    ObjectReadOperation op;
-    op.read(1, 0, &bl, NULL);
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate(
-      "foo", completion, &op,
-      librados::OPERATION_IGNORE_CACHE, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(-ENOENT, completion->get_return_value());
-    completion->release();
-  }
-  // head is still there...
-  ioctx.snap_set_read(librados::SNAP_HEAD);
-  {
-    bufferlist bl;
-    ObjectReadOperation op;
-    op.read(1, 0, &bl, NULL);
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate(
-      "foo", completion, &op,
-      librados::OPERATION_IGNORE_CACHE, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-
-  // promote head + snap of bar
-  ioctx.snap_set_read(librados::SNAP_HEAD);
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("bar", bl, 1, 0));
-    ASSERT_EQ('c', bl[0]);
-  }
-  ioctx.snap_set_read(my_snaps[0]);
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("bar", bl, 1, 0));
-    ASSERT_EQ('h', bl[0]);
-  }
-
-  // evict bar head (fail)
-  ioctx.snap_set_read(librados::SNAP_HEAD);
-  {
-    ObjectReadOperation op;
-    op.cache_evict();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate(
-      "bar", completion, &op,
-      librados::OPERATION_IGNORE_CACHE, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(-EBUSY, completion->get_return_value());
-    completion->release();
-  }
-
-  // evict bar snap
-  ioctx.snap_set_read(my_snaps[0]);
-  {
-    ObjectReadOperation op;
-    op.cache_evict();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate(
-      "bar", completion, &op,
-      librados::OPERATION_IGNORE_CACHE, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-  // ...and then head
-  ioctx.snap_set_read(librados::SNAP_HEAD);
-  {
-    bufferlist bl;
-    ObjectReadOperation op;
-    op.read(1, 0, &bl, NULL);
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate(
-      "bar", completion, &op,
-      librados::OPERATION_IGNORE_CACHE, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-  {
-    ObjectReadOperation op;
-    op.cache_evict();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate(
-      "bar", completion, &op,
-      librados::OPERATION_IGNORE_CACHE, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-
-  // cleanup
-  ioctx.selfmanaged_snap_remove(my_snaps[0]);
-}
-
-TEST_F(LibRadosTwoPoolsECPP, TryFlush) {
-  // configure cache
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name +
-    "\", \"force_nonempty\": \"--force-nonempty\" }",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
-    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
-    "\", \"mode\": \"writeback\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  // create object
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-
-  // verify the object is present in the cache tier
-  {
-    NObjectIterator it = cache_ioctx.nobjects_begin();
-    ASSERT_TRUE(it != cache_ioctx.nobjects_end());
-    ASSERT_TRUE(it->get_oid() == string("foo"));
-    ++it;
-    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
-  }
-
-  // verify the object is NOT present in the base tier
-  {
-    NObjectIterator it = ioctx.nobjects_begin();
-    ASSERT_TRUE(it == ioctx.nobjects_end());
-  }
-
-  // verify dirty
-  {
-    bool dirty = false;
-    int r = -1;
-    ObjectReadOperation op;
-    op.is_dirty(&dirty, &r);
-    ASSERT_EQ(0, cache_ioctx.operate("foo", &op, NULL));
-    ASSERT_TRUE(dirty);
-    ASSERT_EQ(0, r);
-  }
-
-  // pin
-  {
-    ObjectWriteOperation op;
-    op.cache_pin();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-
-  // flush the pinned object with -EPERM
-  {
-    ObjectReadOperation op;
-    op.cache_try_flush();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate(
-      "foo", completion, &op,
-      librados::OPERATION_IGNORE_OVERLAY |
-      librados::OPERATION_SKIPRWLOCKS, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(-EPERM, completion->get_return_value());
-    completion->release();
-  }
-
-  // unpin
-  {
-    ObjectWriteOperation op;
-    op.cache_unpin();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-
-  // flush
-  {
-    ObjectReadOperation op;
-    op.cache_try_flush();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate(
-      "foo", completion, &op,
-      librados::OPERATION_IGNORE_OVERLAY |
-      librados::OPERATION_SKIPRWLOCKS, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-
-  // verify clean
-  {
-    bool dirty = false;
-    int r = -1;
-    ObjectReadOperation op;
-    op.is_dirty(&dirty, &r);
-    ASSERT_EQ(0, cache_ioctx.operate("foo", &op, NULL));
-    ASSERT_FALSE(dirty);
-    ASSERT_EQ(0, r);
-  }
-
-  // verify in base tier
-  {
-    NObjectIterator it = ioctx.nobjects_begin();
-    ASSERT_TRUE(it != ioctx.nobjects_end());
-    ASSERT_TRUE(it->get_oid() == string("foo"));
-    ++it;
-    ASSERT_TRUE(it == ioctx.nobjects_end());
-  }
-
-  // evict it
-  {
-    ObjectReadOperation op;
-    op.cache_evict();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate(
-        "foo", completion, &op, librados::OPERATION_IGNORE_CACHE, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-
-  // verify no longer in cache tier
-  {
-    NObjectIterator it = cache_ioctx.nobjects_begin();
-    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
-  }
-}
-
-TEST_F(LibRadosTwoPoolsECPP, FailedFlush) {
-  // configure cache
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name +
-    "\", \"force_nonempty\": \"--force-nonempty\" }",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
-    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
-    "\", \"mode\": \"writeback\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  // create object
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-
-  // verify the object is present in the cache tier
-  {
-    NObjectIterator it = cache_ioctx.nobjects_begin();
-    ASSERT_TRUE(it != cache_ioctx.nobjects_end());
-    ASSERT_TRUE(it->get_oid() == string("foo"));
-    ++it;
-    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
-  }
-
-  // verify the object is NOT present in the base tier
-  {
-    NObjectIterator it = ioctx.nobjects_begin();
-    ASSERT_TRUE(it == ioctx.nobjects_end());
-  }
-
-  // set omap
-  {
-    ObjectWriteOperation op;
-    std::map<std::string, bufferlist> omap;
-    omap["somekey"] = bufferlist();
-    op.omap_set(omap);
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-
-  // flush
-  {
-    ObjectReadOperation op;
-    op.cache_flush();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate(
-      "foo", completion, &op,
-      librados::OPERATION_IGNORE_OVERLAY, NULL));
-    completion->wait_for_safe();
-    ASSERT_NE(0, completion->get_return_value());
-    completion->release();
-  }
-
-  // get omap
-  {
-    ObjectReadOperation op;
-    bufferlist bl;
-    int prval = 0;
-    std::set<std::string> keys;
-    keys.insert("somekey");
-    std::map<std::string, bufferlist> map;
-
-    op.omap_get_vals_by_keys(keys, &map, &prval);
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op, &bl));
-    sleep(5);
-    bool completed = completion->is_complete();
-    if( !completed ) {
-      cache_ioctx.aio_cancel(completion); 
-      std::cerr << "Most probably test case will hang here, please reset manually" << std::endl;
-      ASSERT_TRUE(completed); //in fact we are locked forever at test case shutdown unless fix for http://tracker.ceph.com/issues/14511 is applied. Seems there is no workaround for that
-    }
-    completion->release();
-  }
-  // verify still not in base tier
-  {
-    ASSERT_TRUE(ioctx.nobjects_begin() == ioctx.nobjects_end());
-  }
-  // erase it
-  {
-    ObjectWriteOperation op;
-    op.remove();
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-  // flush whiteout
-  {
-    ObjectReadOperation op;
-    op.cache_flush();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate(
-      "foo", completion, &op,
-      librados::OPERATION_IGNORE_OVERLAY, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-  // evict
-  {
-    ObjectReadOperation op;
-    op.cache_evict();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate(
-      "foo", completion, &op, librados::OPERATION_IGNORE_CACHE, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-
-  // verify no longer in cache tier
-  {
-    NObjectIterator it = cache_ioctx.nobjects_begin();
-    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
-  }
-  // or base tier
-  {
-    NObjectIterator it = ioctx.nobjects_begin();
-    ASSERT_TRUE(it == ioctx.nobjects_end());
-  }
-}
-
-TEST_F(LibRadosTwoPoolsECPP, Flush) {
-  // configure cache
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name +
-    "\", \"force_nonempty\": \"--force-nonempty\" }",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
-    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
-    "\", \"mode\": \"writeback\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  uint64_t user_version = 0;
-
-  // create object
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-
-  // verify the object is present in the cache tier
-  {
-    NObjectIterator it = cache_ioctx.nobjects_begin();
-    ASSERT_TRUE(it != cache_ioctx.nobjects_end());
-    ASSERT_TRUE(it->get_oid() == string("foo"));
-    ++it;
-    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
-  }
-
-  // verify the object is NOT present in the base tier
-  {
-    NObjectIterator it = ioctx.nobjects_begin();
-    ASSERT_TRUE(it == ioctx.nobjects_end());
-  }
-
-  // verify dirty
-  {
-    bool dirty = false;
-    int r = -1;
-    ObjectReadOperation op;
-    op.is_dirty(&dirty, &r);
-    ASSERT_EQ(0, cache_ioctx.operate("foo", &op, NULL));
-    ASSERT_TRUE(dirty);
-    ASSERT_EQ(0, r);
-    user_version = cache_ioctx.get_last_version();
-  }
-
-  // pin
-  {
-    ObjectWriteOperation op;
-    op.cache_pin();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-
-  // flush the pinned object with -EPERM
-  {
-    ObjectReadOperation op;
-    op.cache_try_flush();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate(
-      "foo", completion, &op,
-      librados::OPERATION_IGNORE_OVERLAY |
-      librados::OPERATION_SKIPRWLOCKS, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(-EPERM, completion->get_return_value());
-    completion->release();
-  }
-
-  // unpin
-  {
-    ObjectWriteOperation op;
-    op.cache_unpin();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-
-  // flush
-  {
-    ObjectReadOperation op;
-    op.cache_flush();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate(
-      "foo", completion, &op,
-      librados::OPERATION_IGNORE_OVERLAY, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-
-  // verify clean
-  {
-    bool dirty = false;
-    int r = -1;
-    ObjectReadOperation op;
-    op.is_dirty(&dirty, &r);
-    ASSERT_EQ(0, cache_ioctx.operate("foo", &op, NULL));
-    ASSERT_FALSE(dirty);
-    ASSERT_EQ(0, r);
-  }
-
-  // verify in base tier
-  {
-    NObjectIterator it = ioctx.nobjects_begin();
-    ASSERT_TRUE(it != ioctx.nobjects_end());
-    ASSERT_TRUE(it->get_oid() == string("foo"));
-    ++it;
-    ASSERT_TRUE(it == ioctx.nobjects_end());
-  }
-
-  // evict it
-  {
-    ObjectReadOperation op;
-    op.cache_evict();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate(
-        "foo", completion, &op, librados::OPERATION_IGNORE_CACHE, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-
-  // verify no longer in cache tier
-  {
-    NObjectIterator it = cache_ioctx.nobjects_begin();
-    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
-  }
-
-  // read it again and verify the version is consistent
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, cache_ioctx.read("foo", bl, 1, 0));
-    ASSERT_EQ(user_version, cache_ioctx.get_last_version());
-  }
-
-  // erase it
-  {
-    ObjectWriteOperation op;
-    op.remove();
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-
-  // flush whiteout
-  {
-    ObjectReadOperation op;
-    op.cache_flush();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate(
-      "foo", completion, &op,
-      librados::OPERATION_IGNORE_OVERLAY, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-
-  // evict
-  {
-    ObjectReadOperation op;
-    op.cache_evict();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate(
-        "foo", completion, &op, librados::OPERATION_IGNORE_CACHE, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-
-  // verify no longer in cache tier
-  {
-    NObjectIterator it = cache_ioctx.nobjects_begin();
-    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
-  }
-  // or base tier
-  {
-    NObjectIterator it = ioctx.nobjects_begin();
-    ASSERT_TRUE(it == ioctx.nobjects_end());
-  }
-}
-
-TEST_F(LibRadosTwoPoolsECPP, FlushSnap) {
-  // configure cache
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name +
-    "\", \"force_nonempty\": \"--force-nonempty\" }",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
-    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
-    "\", \"mode\": \"writeback\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  // create object
-  {
-    bufferlist bl;
-    bl.append("a");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-
-  // create a snapshot, clone
-  vector<uint64_t> my_snaps(1);
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
-                                                        my_snaps));
-  {
-    bufferlist bl;
-    bl.append("b");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-
-  // and another
-  my_snaps.resize(2);
-  my_snaps[1] = my_snaps[0];
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
-  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
-                                                        my_snaps));
-  {
-    bufferlist bl;
-    bl.append("c");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-
-  // verify the object is present in the cache tier
-  {
-    NObjectIterator it = cache_ioctx.nobjects_begin();
-    ASSERT_TRUE(it != cache_ioctx.nobjects_end());
-    ASSERT_TRUE(it->get_oid() == string("foo"));
-    ++it;
-    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
-  }
-
-  // verify the object is NOT present in the base tier
-  {
-    NObjectIterator it = ioctx.nobjects_begin();
-    ASSERT_TRUE(it == ioctx.nobjects_end());
-  }
-
-  // flush on head (should fail)
-  ioctx.snap_set_read(librados::SNAP_HEAD);
-  {
-    ObjectReadOperation op;
-    op.cache_flush();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate(
-      "foo", completion, &op,
-      librados::OPERATION_IGNORE_CACHE, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(-EBUSY, completion->get_return_value());
-    completion->release();
-  }
-  // flush on recent snap (should fail)
-  ioctx.snap_set_read(my_snaps[0]);
-  {
-    ObjectReadOperation op;
-    op.cache_flush();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate(
-      "foo", completion, &op,
-      librados::OPERATION_IGNORE_CACHE, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(-EBUSY, completion->get_return_value());
-    completion->release();
-  }
-  // flush on oldest snap
-  ioctx.snap_set_read(my_snaps[1]);
-  {
-    ObjectReadOperation op;
-    op.cache_flush();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate(
-      "foo", completion, &op,
-      librados::OPERATION_IGNORE_CACHE, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-  // flush on next oldest snap
-  ioctx.snap_set_read(my_snaps[0]);
-  {
-    ObjectReadOperation op;
-    op.cache_flush();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate(
-      "foo", completion, &op,
-      librados::OPERATION_IGNORE_CACHE, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-  // flush on head
-  ioctx.snap_set_read(librados::SNAP_HEAD);
-  {
-    ObjectReadOperation op;
-    op.cache_flush();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate(
-      "foo", completion, &op,
-      librados::OPERATION_IGNORE_CACHE, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-
-  // verify i can read the snaps from the cache pool
-  ioctx.snap_set_read(librados::SNAP_HEAD);
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
-    ASSERT_EQ('c', bl[0]);
-  }
-  ioctx.snap_set_read(my_snaps[0]);
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
-    ASSERT_EQ('b', bl[0]);
-  }
-  ioctx.snap_set_read(my_snaps[1]);
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
-    ASSERT_EQ('a', bl[0]);
-  }
-
-  // tear down tiers
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier remove-overlay\", \"pool\": \"" + pool_name +
-    "\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  // verify i can read the snaps from the base pool
-  ioctx.snap_set_read(librados::SNAP_HEAD);
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
-    ASSERT_EQ('c', bl[0]);
-  }
-  ioctx.snap_set_read(my_snaps[0]);
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
-    ASSERT_EQ('b', bl[0]);
-  }
-  ioctx.snap_set_read(my_snaps[1]);
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
-    ASSERT_EQ('a', bl[0]);
-  }
-
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
-    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-  cluster.wait_for_latest_osdmap();
-
-  // cleanup
-  ioctx.selfmanaged_snap_remove(my_snaps[0]);
-}
-
-TEST_F(LibRadosTierECPP, FlushWriteRaces) {
-  Rados cluster;
-  std::string pool_name = get_temp_pool_name();
-  std::string cache_pool_name = pool_name + "-cache";
-  ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
-  ASSERT_EQ(0, cluster.pool_create(cache_pool_name.c_str()));
-  IoCtx cache_ioctx;
-  ASSERT_EQ(0, cluster.ioctx_create(cache_pool_name.c_str(), cache_ioctx));
-  cache_ioctx.application_enable("rados", true);
-  IoCtx ioctx;
-  ASSERT_EQ(0, cluster.ioctx_create(pool_name.c_str(), ioctx));
-
-  // configure cache
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
-    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
-    "\", \"mode\": \"writeback\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  // create/dirty object
-  bufferlist bl;
-  bl.append("hi there");
-  {
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-
-  // flush + write
-  {
-    ObjectReadOperation op;
-    op.cache_flush();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate(
-      "foo", completion, &op,
-      librados::OPERATION_IGNORE_OVERLAY, NULL));
-
-    ObjectWriteOperation op2;
-    op2.write_full(bl);
-    librados::AioCompletion *completion2 = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate(
-      "foo", completion2, &op2, 0));
-
-    completion->wait_for_safe();
-    completion2->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    ASSERT_EQ(0, completion2->get_return_value());
-    completion->release();
-    completion2->release();
-  }
-
-  int tries = 1000;
-  do {
-    // create/dirty object
-    {
-      bufferlist bl;
-      bl.append("hi there");
-      ObjectWriteOperation op;
-      op.write_full(bl);
-      ASSERT_EQ(0, ioctx.operate("foo", &op));
-    }
-
-    // try-flush + write
-    {
-      ObjectReadOperation op;
-      op.cache_try_flush();
-      librados::AioCompletion *completion = cluster.aio_create_completion();
-      ASSERT_EQ(0, cache_ioctx.aio_operate(
-        "foo", completion, &op,
-       librados::OPERATION_IGNORE_OVERLAY |
-       librados::OPERATION_SKIPRWLOCKS, NULL));
-
-      ObjectWriteOperation op2;
-      op2.write_full(bl);
-      librados::AioCompletion *completion2 = cluster.aio_create_completion();
-      ASSERT_EQ(0, ioctx.aio_operate("foo", completion2, &op2, 0));
-
-      completion->wait_for_safe();
-      completion2->wait_for_safe();
-      int r = completion->get_return_value();
-      ASSERT_TRUE(r == -EBUSY || r == 0);
-      ASSERT_EQ(0, completion2->get_return_value());
-      completion->release();
-      completion2->release();
-      if (r == -EBUSY)
-       break;
-      cout << "didn't get EBUSY, trying again" << std::endl;
-    }
-    ASSERT_TRUE(--tries);
-  } while (true);
-
-  // tear down tiers
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier remove-overlay\", \"pool\": \"" + pool_name +
-    "\"}",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle before next test
-  cluster.wait_for_latest_osdmap();
-
-  ASSERT_EQ(0, cluster.pool_delete(cache_pool_name.c_str()));
-  ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
-}
-
-TEST_F(LibRadosTwoPoolsECPP, FlushTryFlushRaces) {
-  // configure cache
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name +
-    "\", \"force_nonempty\": \"--force-nonempty\" }",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
-    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
-    "\", \"mode\": \"writeback\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  // create/dirty object
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-
-  // flush + flush
-  {
-    ObjectReadOperation op;
-    op.cache_flush();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate(
-      "foo", completion, &op,
-      librados::OPERATION_IGNORE_OVERLAY, NULL));
-
-    ObjectReadOperation op2;
-    op2.cache_flush();
-    librados::AioCompletion *completion2 = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate(
-      "foo", completion2, &op2,
-      librados::OPERATION_IGNORE_OVERLAY, NULL));
-
-    completion->wait_for_safe();
-    completion2->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    ASSERT_EQ(0, completion2->get_return_value());
-    completion->release();
-    completion2->release();
-  }
-
-  // create/dirty object
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-
-  // flush + try-flush
-  {
-    ObjectReadOperation op;
-    op.cache_flush();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate(
-      "foo", completion, &op,
-      librados::OPERATION_IGNORE_OVERLAY, NULL));
-
-    ObjectReadOperation op2;
-    op2.cache_try_flush();
-    librados::AioCompletion *completion2 = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate(
-      "foo", completion2, &op2,
-      librados::OPERATION_IGNORE_OVERLAY |
-      librados::OPERATION_SKIPRWLOCKS, NULL));
-
-    completion->wait_for_safe();
-    completion2->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    ASSERT_EQ(0, completion2->get_return_value());
-    completion->release();
-    completion2->release();
-  }
-
-  // create/dirty object
-  int tries = 1000;
-  do {
-    {
-      bufferlist bl;
-      bl.append("hi there");
-      ObjectWriteOperation op;
-      op.write_full(bl);
-      ASSERT_EQ(0, ioctx.operate("foo", &op));
-    }
-
-    // try-flush + flush
-    //  (flush will not piggyback on try-flush)
-    {
-      ObjectReadOperation op;
-      op.cache_try_flush();
-      librados::AioCompletion *completion = cluster.aio_create_completion();
-      ASSERT_EQ(0, cache_ioctx.aio_operate(
-        "foo", completion, &op,
-       librados::OPERATION_IGNORE_OVERLAY |
-       librados::OPERATION_SKIPRWLOCKS, NULL));
-
-      ObjectReadOperation op2;
-      op2.cache_flush();
-      librados::AioCompletion *completion2 = cluster.aio_create_completion();
-      ASSERT_EQ(0, cache_ioctx.aio_operate(
-        "foo", completion2, &op2,
-       librados::OPERATION_IGNORE_OVERLAY, NULL));
-
-      completion->wait_for_safe();
-      completion2->wait_for_safe();
-      int r = completion->get_return_value();
-      ASSERT_TRUE(r == -EBUSY || r == 0);
-      ASSERT_EQ(0, completion2->get_return_value());
-      completion->release();
-      completion2->release();
-      if (r == -EBUSY)
-       break;
-      cout << "didn't get EBUSY, trying again" << std::endl;
-    }
-    ASSERT_TRUE(--tries);
-  } while (true);
-
-  // create/dirty object
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-
-  // try-flush + try-flush
-  {
-    ObjectReadOperation op;
-    op.cache_try_flush();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate(
-      "foo", completion, &op,
-      librados::OPERATION_IGNORE_OVERLAY |
-      librados::OPERATION_SKIPRWLOCKS, NULL));
-
-    ObjectReadOperation op2;
-    op2.cache_try_flush();
-    librados::AioCompletion *completion2 = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate(
-      "foo", completion2, &op2,
-      librados::OPERATION_IGNORE_OVERLAY |
-      librados::OPERATION_SKIPRWLOCKS, NULL));
-
-    completion->wait_for_safe();
-    completion2->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    ASSERT_EQ(0, completion2->get_return_value());
-    completion->release();
-    completion2->release();
-  }
-}
-
-TEST_F(LibRadosTwoPoolsECPP, TryFlushReadRace) {
-  // configure cache
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name +
-    "\", \"force_nonempty\": \"--force-nonempty\" }",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
-    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
-    "\", \"mode\": \"writeback\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  // create/dirty object
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    bufferptr bp(4000000);  // make it big!
-    bp.zero();
-    bl.append(bp);
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-
-  // start a continuous stream of reads
-  read_ioctx = &ioctx;
-  test_lock.Lock();
-  for (int i = 0; i < max_reads; ++i) {
-    start_flush_read();
-    num_reads++;
-  }
-  test_lock.Unlock();
-
-  // try-flush
-  ObjectReadOperation op;
-  op.cache_try_flush();
-  librados::AioCompletion *completion = cluster.aio_create_completion();
-  ASSERT_EQ(0, cache_ioctx.aio_operate(
-      "foo", completion, &op,
-      librados::OPERATION_IGNORE_OVERLAY |
-      librados::OPERATION_SKIPRWLOCKS, NULL));
-
-  completion->wait_for_safe();
-  ASSERT_EQ(0, completion->get_return_value());
-  completion->release();
-
-  // stop reads
-  test_lock.Lock();
-  max_reads = 0;
-  while (num_reads > 0)
-    cond.Wait(test_lock);
-  test_lock.Unlock();
-}
-
-TEST_F(LibRadosTierECPP, CallForcesPromote) {
-  Rados cluster;
-  std::string pool_name = get_temp_pool_name();
-  std::string cache_pool_name = pool_name + "-cache";
-  ASSERT_EQ("", create_one_ec_pool_pp(pool_name, cluster));
-  ASSERT_EQ(0, cluster.pool_create(cache_pool_name.c_str()));
-  IoCtx cache_ioctx;
-  ASSERT_EQ(0, cluster.ioctx_create(cache_pool_name.c_str(), cache_ioctx));
-  cache_ioctx.application_enable("rados", true);
-  IoCtx ioctx;
-  ASSERT_EQ(0, cluster.ioctx_create(pool_name.c_str(), ioctx));
-
-  // configure cache
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
-    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
-    "\", \"mode\": \"writeback\"}",
-    inbl, NULL, NULL));
-
-  // set things up such that the op would normally be proxied
-  ASSERT_EQ(0, cluster.mon_command(
-             set_pool_str(cache_pool_name, "hit_set_count", 2),
-             inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-             set_pool_str(cache_pool_name, "hit_set_period", 600),
-             inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-             set_pool_str(cache_pool_name, "hit_set_type",
-                          "explicit_object"),
-             inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-             set_pool_str(cache_pool_name, "min_read_recency_for_promote",
-                          "4"),
-             inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  // create/dirty object
-  bufferlist bl;
-  bl.append("hi there");
-  {
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-
-  // flush
-  {
-    ObjectReadOperation op;
-    op.cache_flush();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate(
-      "foo", completion, &op,
-      librados::OPERATION_IGNORE_OVERLAY, NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-
-  // evict
-  {
-    ObjectReadOperation op;
-    op.cache_evict();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op,
-                                        librados::OPERATION_IGNORE_CACHE,
-                                        NULL));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-
-  // call
-  {
-    ObjectReadOperation op;
-    bufferlist bl;
-    op.exec("rbd", "get_id", bl);
-    bufferlist out;
-    // should get EIO (not an rbd object), not -EOPNOTSUPP (we didn't promote)
-    ASSERT_EQ(-5, ioctx.operate("foo", &op, &out));
-  }
-
-  // make sure foo is back in the cache tier
-  {
-    NObjectIterator it = cache_ioctx.nobjects_begin();
-    ASSERT_TRUE(it != cache_ioctx.nobjects_end());
-    ASSERT_TRUE(it->get_oid() == string("foo"));
-    ++it;
-    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
-  }
-
-  // tear down tiers
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier remove-overlay\", \"pool\": \"" + pool_name +
-    "\"}",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle before next test
-  cluster.wait_for_latest_osdmap();
-
-  ASSERT_EQ(0, cluster.pool_delete(cache_pool_name.c_str()));
-  ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
-}
-
-TEST_F(LibRadosTierECPP, HitSetNone) {
-  {
-    list< pair<time_t,time_t> > ls;
-    AioCompletion *c = librados::Rados::aio_create_completion();
-    ASSERT_EQ(0, ioctx.hit_set_list(123, c, &ls));
-    c->wait_for_complete();
-    ASSERT_EQ(0, c->get_return_value());
-    ASSERT_TRUE(ls.empty());
-    c->release();
-  }
-  {
-    bufferlist bl;
-    AioCompletion *c = librados::Rados::aio_create_completion();
-    ASSERT_EQ(0, ioctx.hit_set_get(123, c, 12345, &bl));
-    c->wait_for_complete();
-    ASSERT_EQ(-ENOENT, c->get_return_value());
-    c->release();
-  }
-}
-
-TEST_F(LibRadosTwoPoolsECPP, HitSetRead) {
-  // make it a tier
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name +
-    "\", \"force_nonempty\": \"--force-nonempty\" }",
-    inbl, NULL, NULL));
-
-  // enable hitset tracking for this pool
-  ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_count", 2),
-                                               inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_period", 600),
-                                               inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_type",
-                                               "explicit_object"),
-                                  inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  cache_ioctx.set_namespace("");
-
-  // keep reading until we see our object appear in the HitSet
-  utime_t start = ceph_clock_now();
-  utime_t hard_stop = start + utime_t(600, 0);
-
-  while (true) {
-    utime_t now = ceph_clock_now();
-    ASSERT_TRUE(now < hard_stop);
-
-    string name = "foo";
-    uint32_t hash;
-    ASSERT_EQ(0, cache_ioctx.get_object_hash_position2(name, &hash));
-    hobject_t oid(sobject_t(name, CEPH_NOSNAP), "", hash,
-                 cluster.pool_lookup(cache_pool_name.c_str()), "");
-
-    bufferlist bl;
-    ASSERT_EQ(-ENOENT, cache_ioctx.read("foo", bl, 1, 0));
-
-    bufferlist hbl;
-    AioCompletion *c = librados::Rados::aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.hit_set_get(hash, c, now.sec(), &hbl));
-    c->wait_for_complete();
-    c->release();
-
-    if (hbl.length()) {
-      auto p = hbl.cbegin();
-      HitSet hs;
-      decode(hs, p);
-      if (hs.contains(oid)) {
-       cout << "ok, hit_set contains " << oid << std::endl;
-       break;
-      }
-      cout << "hmm, not in HitSet yet" << std::endl;
-    } else {
-      cout << "hmm, no HitSet yet" << std::endl;
-    }
-
-    sleep(1);
-  }
-}
-
-// disable this test until hitset-get reliably works on EC pools
-#if 0
-TEST_F(LibRadosTierECPP, HitSetWrite) {
-  int num_pg = _get_pg_num(cluster, pool_name);
-  ceph_assert(num_pg > 0);
-
-  // enable hitset tracking for this pool
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(set_pool_str(pool_name, "hit_set_count", 8),
-                                               inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(set_pool_str(pool_name, "hit_set_period", 600),
-                                               inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(set_pool_str(pool_name, "hit_set_type",
-                                               "explicit_hash"),
-                                  inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  ioctx.set_namespace("");
-
-  // do a bunch of writes
-  for (int i=0; i<1000; ++i) {
-    bufferlist bl;
-    bl.append("a");
-    ASSERT_EQ(0, ioctx.write(stringify(i), bl, 1, 0));
-  }
-
-  // get HitSets
-  std::map<int,HitSet> hitsets;
-  for (int i=0; i<num_pg; ++i) {
-    list< pair<time_t,time_t> > ls;
-    AioCompletion *c = librados::Rados::aio_create_completion();
-    ASSERT_EQ(0, ioctx.hit_set_list(i, c, &ls));
-    c->wait_for_complete();
-    c->release();
-    std::cout << "pg " << i << " ls " << ls << std::endl;
-    ASSERT_FALSE(ls.empty());
-
-    // get the latest
-    c = librados::Rados::aio_create_completion();
-    bufferlist bl;
-    ASSERT_EQ(0, ioctx.hit_set_get(i, c, ls.back().first, &bl));
-    c->wait_for_complete();
-    c->release();
-
-    //std::cout << "bl len is " << bl.length() << "\n";
-    //bl.hexdump(std::cout);
-    //std::cout << std::endl;
-
-    auto p = bl.cbegin();
-    decode(hitsets[i], p);
-
-    // cope with racing splits by refreshing pg_num
-    if (i == num_pg - 1)
-      num_pg = _get_pg_num(cluster, pool_name);
-  }
-
-  for (int i=0; i<1000; ++i) {
-    string n = stringify(i);
-    uint32_t hash = ioctx.get_object_hash_position(n);
-    hobject_t oid(sobject_t(n, CEPH_NOSNAP), "", hash,
-                 cluster.pool_lookup(pool_name.c_str()), "");
-    std::cout << "checking for " << oid << std::endl;
-    bool found = false;
-    for (int p=0; p<num_pg; ++p) {
-      if (hitsets[p].contains(oid)) {
-       found = true;
-       break;
-      }
-    }
-    ASSERT_TRUE(found);
-  }
-}
-#endif
-
-TEST_F(LibRadosTwoPoolsECPP, HitSetTrim) {
-  unsigned count = 3;
-  unsigned period = 3;
-
-  // make it a tier
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name +
-    "\", \"force_nonempty\": \"--force-nonempty\" }",
-    inbl, NULL, NULL));
-
-  // enable hitset tracking for this pool
-  ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_count", count),
-                                               inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_period", period),
-                                               inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_type", "bloom"),
-                                  inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_fpp", ".01"),
-                                  inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  cache_ioctx.set_namespace("");
-
-  // do a bunch of writes and make sure the hitsets rotate
-  utime_t start = ceph_clock_now();
-  utime_t hard_stop = start + utime_t(count * period * 50, 0);
-
-  time_t first = 0;
-  int bsize = alignment;
-  char *buf = (char *)new char[bsize];
-  memset(buf, 'f', bsize);
-
-  while (true) {
-    string name = "foo";
-    uint32_t hash;
-    ASSERT_EQ(0, cache_ioctx.get_object_hash_position2(name, &hash));
-    hobject_t oid(sobject_t(name, CEPH_NOSNAP), "", hash, -1, "");
-
-    bufferlist bl;
-    bl.append(buf, bsize);
-    ASSERT_EQ(0, cache_ioctx.append("foo", bl, bsize));
-
-    list<pair<time_t, time_t> > ls;
-    AioCompletion *c = librados::Rados::aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.hit_set_list(hash, c, &ls));
-    c->wait_for_complete();
-    c->release();
-
-    cout << " got ls " << ls << std::endl;
-    if (!ls.empty()) {
-      if (!first) {
-       first = ls.front().first;
-       cout << "first is " << first << std::endl;
-      } else {
-       if (ls.front().first != first) {
-         cout << "first now " << ls.front().first << ", trimmed" << std::endl;
-         break;
-       }
-      }
-    }
-
-    utime_t now = ceph_clock_now();
-    ASSERT_TRUE(now < hard_stop);
-
-    sleep(1);
-  }
-  delete[] buf;
-}
-
-TEST_F(LibRadosTwoPoolsECPP, PromoteOn2ndRead) {
-  // create object
-  for (int i=0; i<20; ++i) {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo" + stringify(i), &op));
-  }
-
-  // configure cache
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name +
-    "\", \"force_nonempty\": \"--force-nonempty\" }",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
-    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
-    "\", \"mode\": \"writeback\"}",
-    inbl, NULL, NULL));
-
-  // enable hitset tracking for this pool
-  ASSERT_EQ(0, cluster.mon_command(
-    set_pool_str(cache_pool_name, "hit_set_count", 2),
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    set_pool_str(cache_pool_name, "hit_set_period", 600),
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    set_pool_str(cache_pool_name, "hit_set_type", "bloom"),
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    set_pool_str(cache_pool_name, "min_read_recency_for_promote", 1),
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    set_pool_str(cache_pool_name, "hit_set_grade_decay_rate", 20),
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    set_pool_str(cache_pool_name, "hit_set_search_last_n", 1),
-    inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  int fake = 0;  // set this to non-zero to test spurious promotion,
-                // e.g. from thrashing
-  int attempt = 0;
-  string obj;
-  while (true) {
-    // 1st read, don't trigger a promote
-    obj = "foo" + stringify(attempt);
-    cout << obj << std::endl;
-    {
-      bufferlist bl;
-      ASSERT_EQ(1, ioctx.read(obj.c_str(), bl, 1, 0));
-      if (--fake >= 0) {
-       sleep(1);
-       ASSERT_EQ(1, ioctx.read(obj.c_str(), bl, 1, 0));
-       sleep(1);
-      }
-    }
-
-    // verify the object is NOT present in the cache tier
-    {
-      bool found = false;
-      NObjectIterator it = cache_ioctx.nobjects_begin();
-      while (it != cache_ioctx.nobjects_end()) {
-       cout << " see " << it->get_oid() << std::endl;
-       if (it->get_oid() == string(obj.c_str())) {
-         found = true;
-         break;
-       }
-       ++it;
-      }
-      if (!found)
-       break;
-    }
-
-    ++attempt;
-    ASSERT_LE(attempt, 20);
-    cout << "hrm, object is present in cache on attempt " << attempt
-        << ", retrying" << std::endl;
-  }
-
-  // Read until the object is present in the cache tier
-  cout << "verifying " << obj << " is eventually promoted" << std::endl;
-  while (true) {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read(obj.c_str(), bl, 1, 0));
-
-    bool there = false;
-    NObjectIterator it = cache_ioctx.nobjects_begin();
-    while (it != cache_ioctx.nobjects_end()) {
-      if (it->get_oid() == string(obj.c_str())) {
-       there = true;
-       break;
-      }
-      ++it;
-    }
-    if (there)
-      break;
-
-    sleep(1);
-  }
-
-  // tear down tiers
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier remove-overlay\", \"pool\": \"" + pool_name +
-    "\"}",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle before next test
-  cluster.wait_for_latest_osdmap();
-}
-
-TEST_F(LibRadosTwoPoolsECPP, ProxyRead) {
-  // create object
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-
-  // configure cache
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name +
-    "\", \"force_nonempty\": \"--force-nonempty\" }",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
-    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
-    "\", \"mode\": \"readproxy\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  // read and verify the object
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
-    ASSERT_EQ('h', bl[0]);
-  }
-
-  // Verify 10 times the object is NOT present in the cache tier
-  uint32_t i = 0;
-  while (i++ < 10) {
-    NObjectIterator it = cache_ioctx.nobjects_begin();
-    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
-    sleep(1);
-  }
-
-  // tear down tiers
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier remove-overlay\", \"pool\": \"" + pool_name +
-    "\"}",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle before next test
-  cluster.wait_for_latest_osdmap();
-}
-
-TEST_F(LibRadosTwoPoolsECPP, CachePin) {
-  // create object
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("bar", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("baz", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("bam", &op));
-  }
-
-  // configure cache
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name +
-    "\", \"force_nonempty\": \"--force-nonempty\" }",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
-    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
-    "\", \"mode\": \"writeback\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  // read, trigger promote
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
-    ASSERT_EQ(1, ioctx.read("bar", bl, 1, 0));
-    ASSERT_EQ(1, ioctx.read("baz", bl, 1, 0));
-    ASSERT_EQ(1, ioctx.read("bam", bl, 1, 0));
-  }
-
-  // verify the objects are present in the cache tier
-  {
-    NObjectIterator it = cache_ioctx.nobjects_begin();
-    ASSERT_TRUE(it != cache_ioctx.nobjects_end());
-    for (uint32_t i = 0; i < 4; i++) {
-      ASSERT_TRUE(it->get_oid() == string("foo") ||
-                  it->get_oid() == string("bar") ||
-                  it->get_oid() == string("baz") ||
-                  it->get_oid() == string("bam"));
-      ++it;
-    }
-    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
-  }
-
-  // pin objects
-  {
-    ObjectWriteOperation op;
-    op.cache_pin();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-  {
-    ObjectWriteOperation op;
-    op.cache_pin();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, cache_ioctx.aio_operate("baz", completion, &op));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-
-  // enable agent
-  ASSERT_EQ(0, cluster.mon_command(
-    set_pool_str(cache_pool_name, "hit_set_count", 2),
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    set_pool_str(cache_pool_name, "hit_set_period", 600),
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    set_pool_str(cache_pool_name, "hit_set_type", "bloom"),
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    set_pool_str(cache_pool_name, "min_read_recency_for_promote", 1),
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    set_pool_str(cache_pool_name, "target_max_objects", 1),
-    inbl, NULL, NULL));
-
-  sleep(10);
-
-  // Verify the pinned object 'foo' is not flushed/evicted
-  uint32_t count = 0;
-  while (true) {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("baz", bl, 1, 0));
-
-    count = 0;
-    NObjectIterator it = cache_ioctx.nobjects_begin();
-    while (it != cache_ioctx.nobjects_end()) {
-      ASSERT_TRUE(it->get_oid() == string("foo") ||
-                  it->get_oid() == string("bar") ||
-                  it->get_oid() == string("baz") ||
-                  it->get_oid() == string("bam"));
-      ++count;
-      ++it;
-    }
-    if (count == 2) {
-      ASSERT_TRUE(it->get_oid() == string("foo") ||
-                  it->get_oid() == string("baz"));
-      break;
-    }
-
-    sleep(1);
-  }
-
-  // tear down tiers
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier remove-overlay\", \"pool\": \"" + pool_name +
-    "\"}",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle before next test
-  cluster.wait_for_latest_osdmap();
-}
-TEST_F(LibRadosTwoPoolsECPP, SetRedirectRead) {
-  // skip test if not yet luminous
-  {
-    bufferlist inbl, outbl;
-    ASSERT_EQ(0, cluster.mon_command(
-               "{\"prefix\": \"osd dump\"}",
-               inbl, &outbl, NULL));
-    string s(outbl.c_str(), outbl.length());
-    if (s.find("luminous") == std::string::npos) {
-      cout << "cluster is not yet luminous, skipping test" << std::endl;
-      return;
-    }
-  }
-
-  // create object
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, cache_ioctx.operate("bar", &op));
-  }
-
-  // configure tier
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name +
-    "\", \"force_nonempty\": \"--force-nonempty\" }",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  {
-    ObjectWriteOperation op;
-    op.set_redirect("bar", cache_ioctx, 0);
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate("foo", completion, &op));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-  // read and verify the object
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
-    ASSERT_EQ('t', bl[0]);
-  }
-
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle before next test
-  cluster.wait_for_latest_osdmap();
-}
-
-TEST_F(LibRadosTwoPoolsECPP, SetChunkRead) {
-  // skip test if not yet mimic
-  {
-    bufferlist inbl, outbl;
-    ASSERT_EQ(0, cluster.mon_command(
-               "{\"prefix\": \"osd dump\"}",
-               inbl, &outbl, NULL));
-    string s(outbl.c_str(), outbl.length());
-    if (s.find("mimic") == std::string::npos) {
-      cout << "cluster is not yet mimic, skipping test" << std::endl;
-      return;
-    }
-  }
-
-  // create object
-  {
-    ObjectWriteOperation op;
-    op.create(true);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, cache_ioctx.operate("bar", &op));
-  }
-
-  // configure tier
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name +
-    "\", \"force_nonempty\": \"--force-nonempty\" }",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  // set_chunk
-  {
-    ObjectWriteOperation op;
-    op.set_chunk(0, 8, cache_ioctx, "bar", 0);
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate("foo", completion, &op));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-
-  // make all chunks dirty --> full flush --> all chunks are evicted
-  {
-    bufferlist bl;
-    bl.append("There hi");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-
-  // read and verify the object
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
-    ASSERT_EQ('T', bl[0]);
-  }
-
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle before next test
-  cluster.wait_for_latest_osdmap();
-}
-
-TEST_F(LibRadosTwoPoolsECPP, ManifestPromoteRead) {
-  // skip test if not yet mimic
-  {
-    bufferlist inbl, outbl;
-    ASSERT_EQ(0, cluster.mon_command(
-               "{\"prefix\": \"osd dump\"}",
-               inbl, &outbl, NULL));
-    string s(outbl.c_str(), outbl.length());
-    if (s.find("mimic") == std::string::npos) {
-      cout << "cluster is not yet mimic, skipping test" << std::endl;
-      return;
-    }
-  }
-
-  // create object
-  {
-    bufferlist bl;
-    bl.append("hi there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, ioctx.operate("foo", &op));
-  }
-  {
-    ObjectWriteOperation op;
-    op.create(true);
-    ASSERT_EQ(0, ioctx.operate("foo-chunk", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("HI there");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, cache_ioctx.operate("bar", &op));
-  }
-  {
-    bufferlist bl;
-    bl.append("BASE CHUNK");
-    ObjectWriteOperation op;
-    op.write_full(bl);
-    ASSERT_EQ(0, cache_ioctx.operate("bar-chunk", &op));
-  }
-
-  // configure tier
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name +
-    "\", \"force_nonempty\": \"--force-nonempty\" }",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  // set-redirect
-  {
-    ObjectWriteOperation op;
-    op.set_redirect("bar", cache_ioctx, 0);
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate("foo", completion, &op));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-  // set-chunk
-  {
-    ObjectWriteOperation op;
-    op.set_chunk(0, 10, cache_ioctx, "bar-chunk", 0);
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate("foo-chunk", completion, &op));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-  // promote
-  {
-    ObjectWriteOperation op;
-    op.tier_promote();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate("foo", completion, &op));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-  // read and verify the object (redirect)
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
-    ASSERT_EQ('H', bl[0]);
-  }
-  // promote
-  {
-    ObjectWriteOperation op;
-    op.tier_promote();
-    librados::AioCompletion *completion = cluster.aio_create_completion();
-    ASSERT_EQ(0, ioctx.aio_operate("foo-chunk", completion, &op));
-    completion->wait_for_safe();
-    ASSERT_EQ(0, completion->get_return_value());
-    completion->release();
-  }
-  // read and verify the object
-  {
-    bufferlist bl;
-    ASSERT_EQ(1, ioctx.read("foo-chunk", bl, 1, 0));
-    ASSERT_EQ('B', bl[0]);
-  }
-
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-
-  // wait for maps to settle before next test
-  cluster.wait_for_latest_osdmap();
-}
-
-TEST_F(LibRadosTwoPoolsPP, PropagateBaseTierError) {
-  // write object  to base tier
-  bufferlist omap_bl;
-  encode(static_cast<uint32_t>(0U), omap_bl);
-
-  ObjectWriteOperation op1;
-  op1.omap_set({{"somekey", omap_bl}});
-  ASSERT_EQ(0, ioctx.operate("propagate-base-tier-error", &op1));
-
-  // configure cache
-  bufferlist inbl;
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
-    "\", \"tierpool\": \"" + cache_pool_name +
-    "\", \"force_nonempty\": \"--force-nonempty\" }",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
-    "\", \"mode\": \"writeback\"}",
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
-    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
-    inbl, NULL, NULL));
-
-  ASSERT_EQ(0, cluster.mon_command(
-    set_pool_str(cache_pool_name, "hit_set_type", "bloom"),
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    set_pool_str(cache_pool_name, "hit_set_count", 1),
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    set_pool_str(cache_pool_name, "hit_set_period", 600),
-    inbl, NULL, NULL));
-  ASSERT_EQ(0, cluster.mon_command(
-    set_pool_str(cache_pool_name, "target_max_objects", 250),
-    inbl, NULL, NULL));
-
-  // wait for maps to settle
-  cluster.wait_for_latest_osdmap();
-
-  // guarded op should fail so expect error to propagate to cache tier
-  bufferlist test_omap_bl;
-  encode(static_cast<uint32_t>(1U), test_omap_bl);
-
-  ObjectWriteOperation op2;
-  op2.omap_cmp({{"somekey", {test_omap_bl, CEPH_OSD_CMPXATTR_OP_EQ}}}, nullptr);
-  op2.omap_set({{"somekey", test_omap_bl}});
-
-  ASSERT_EQ(-ECANCELED, ioctx.operate("propagate-base-tier-error", &op2));
-}
diff --git a/src/test/librados/tier_cxx.cc b/src/test/librados/tier_cxx.cc
new file mode 100644 (file)
index 0000000..e6acd30
--- /dev/null
@@ -0,0 +1,6489 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#include "gtest/gtest.h"
+
+#include "mds/mdstypes.h"
+#include "include/buffer.h"
+#include "include/rbd_types.h"
+#include "include/rados/librados.hpp"
+#include "include/stringify.h"
+#include "include/types.h"
+#include "global/global_context.h"
+#include "common/Cond.h"
+#include "test/librados/test_cxx.h"
+#include "test/librados/testcase_cxx.h"
+#include "json_spirit/json_spirit.h"
+#include "cls/cas/cls_cas_ops.h"
+
+#include "osd/HitSet.h"
+
+#include <errno.h>
+#include <map>
+#include <sstream>
+#include <string>
+
+using namespace librados;
+using std::map;
+using std::ostringstream;
+using std::string;
+
+typedef RadosTestPP LibRadosTierPP;
+typedef RadosTestECPP LibRadosTierECPP;
+
+void flush_evict_all(librados::Rados& cluster, librados::IoCtx& cache_ioctx)
+{
+  bufferlist inbl;
+  cache_ioctx.set_namespace(all_nspaces);
+  for (NObjectIterator it = cache_ioctx.nobjects_begin();
+       it != cache_ioctx.nobjects_end(); ++it) {
+    cache_ioctx.locator_set_key(it->get_locator());
+    cache_ioctx.set_namespace(it->get_nspace());
+    {
+      ObjectReadOperation op;
+      op.cache_flush();
+      librados::AioCompletion *completion = cluster.aio_create_completion();
+      cache_ioctx.aio_operate(
+        it->get_oid(), completion, &op,
+       librados::OPERATION_IGNORE_OVERLAY, NULL);
+      completion->wait_for_safe();
+      completion->get_return_value();
+      completion->release();
+    }
+    {
+      ObjectReadOperation op;
+      op.cache_evict();
+      librados::AioCompletion *completion = cluster.aio_create_completion();
+      cache_ioctx.aio_operate(
+        it->get_oid(), completion, &op,
+       librados::OPERATION_IGNORE_OVERLAY, NULL);
+      completion->wait_for_safe();
+      completion->get_return_value();
+      completion->release();
+    }
+  }
+}
+
+class LibRadosTwoPoolsPP : public RadosTestPP
+{
+public:
+  LibRadosTwoPoolsPP() {};
+  ~LibRadosTwoPoolsPP() override {};
+protected:
+  static void SetUpTestCase() {
+    pool_name = get_temp_pool_name();
+    ASSERT_EQ("", create_one_pool_pp(pool_name, s_cluster));
+  }
+  static void TearDownTestCase() {
+    ASSERT_EQ(0, destroy_one_pool_pp(pool_name, s_cluster));
+  }
+  static std::string cache_pool_name;
+
+  void SetUp() override {
+    cache_pool_name = get_temp_pool_name();
+    ASSERT_EQ(0, s_cluster.pool_create(cache_pool_name.c_str()));
+    RadosTestPP::SetUp();
+
+    ASSERT_EQ(0, cluster.ioctx_create(cache_pool_name.c_str(), cache_ioctx));
+    cache_ioctx.application_enable("rados", true);
+    cache_ioctx.set_namespace(nspace);
+  }
+  void TearDown() override {
+    // flush + evict cache
+    flush_evict_all(cluster, cache_ioctx);
+
+    bufferlist inbl;
+    // tear down tiers
+    ASSERT_EQ(0, cluster.mon_command(
+      "{\"prefix\": \"osd tier remove-overlay\", \"pool\": \"" + pool_name +
+      "\"}",
+      inbl, NULL, NULL));
+    ASSERT_EQ(0, cluster.mon_command(
+      "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
+      "\", \"tierpool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+
+    // wait for maps to settle before next test
+    cluster.wait_for_latest_osdmap();
+
+    RadosTestPP::TearDown();
+
+    cleanup_default_namespace(cache_ioctx);
+    cleanup_namespace(cache_ioctx, nspace);
+
+    cache_ioctx.close();
+    ASSERT_EQ(0, s_cluster.pool_delete(cache_pool_name.c_str()));
+  }
+  librados::IoCtx cache_ioctx;
+};
+
+class Completions
+{
+public:
+  Completions() = default;
+  librados::AioCompletion* getCompletion() {
+    librados::AioCompletion* comp = librados::Rados::aio_create_completion();
+    m_completions.push_back(comp);
+    return comp;
+  }
+
+  ~Completions() {
+    for (auto& comp : m_completions) {
+      comp->release();
+    }
+  }
+
+private:
+  vector<librados::AioCompletion *> m_completions;
+};
+
+Completions completions;
+
+std::string LibRadosTwoPoolsPP::cache_pool_name;
+
+TEST_F(LibRadosTierPP, Dirty) {
+  {
+    ObjectWriteOperation op;
+    op.undirty();
+    ASSERT_EQ(0, ioctx.operate("foo", &op)); // still get 0 if it dne
+  }
+  {
+    ObjectWriteOperation op;
+    op.create(true);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+  {
+    bool dirty = false;
+    int r = -1;
+    ObjectReadOperation op;
+    op.is_dirty(&dirty, &r);
+    ASSERT_EQ(0, ioctx.operate("foo", &op, NULL));
+    ASSERT_TRUE(dirty);
+    ASSERT_EQ(0, r);
+  }
+  {
+    ObjectWriteOperation op;
+    op.undirty();
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+  {
+    ObjectWriteOperation op;
+    op.undirty();
+    ASSERT_EQ(0, ioctx.operate("foo", &op));  // still 0 if already clean
+  }
+  {
+    bool dirty = false;
+    int r = -1;
+    ObjectReadOperation op;
+    op.is_dirty(&dirty, &r);
+    ASSERT_EQ(0, ioctx.operate("foo", &op, NULL));
+    ASSERT_FALSE(dirty);
+    ASSERT_EQ(0, r);
+  }
+  {
+    ObjectWriteOperation op;
+    op.truncate(0);  // still a write even tho it is a no-op
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+  {
+    bool dirty = false;
+    int r = -1;
+    ObjectReadOperation op;
+    op.is_dirty(&dirty, &r);
+    ASSERT_EQ(0, ioctx.operate("foo", &op, NULL));
+    ASSERT_TRUE(dirty);
+    ASSERT_EQ(0, r);
+  }
+}
+
+TEST_F(LibRadosTwoPoolsPP, Overlay) {
+  // create objects
+  {
+    bufferlist bl;
+    bl.append("base");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("cache");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, cache_ioctx.operate("foo", &op));
+  }
+
+  // configure cache
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name +
+    "\", \"force_nonempty\": \"--force-nonempty\" }",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  // by default, the overlay sends us to cache pool
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+    ASSERT_EQ('c', bl[0]);
+  }
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, cache_ioctx.read("foo", bl, 1, 0));
+    ASSERT_EQ('c', bl[0]);
+  }
+
+  // unless we say otherwise
+  {
+    bufferlist bl;
+    ObjectReadOperation op;
+    op.read(0, 1, &bl, NULL);
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate(
+       "foo", completion, &op,
+       librados::OPERATION_IGNORE_OVERLAY, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+    ASSERT_EQ('b', bl[0]);
+  }
+}
+
+TEST_F(LibRadosTwoPoolsPP, Promote) {
+  // create object
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+
+  // configure cache
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name +
+    "\", \"force_nonempty\": \"--force-nonempty\" }",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+    "\", \"mode\": \"writeback\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  // read, trigger a promote
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+  }
+
+  // read, trigger a whiteout
+  {
+    bufferlist bl;
+    ASSERT_EQ(-ENOENT, ioctx.read("bar", bl, 1, 0));
+    ASSERT_EQ(-ENOENT, ioctx.read("bar", bl, 1, 0));
+  }
+
+  // verify the object is present in the cache tier
+  {
+    NObjectIterator it = cache_ioctx.nobjects_begin();
+    ASSERT_TRUE(it != cache_ioctx.nobjects_end());
+    ASSERT_TRUE(it->get_oid() == string("foo") || it->get_oid() == string("bar"));
+    ++it;
+    ASSERT_TRUE(it->get_oid() == string("foo") || it->get_oid() == string("bar"));
+    ++it;
+    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+  }
+}
+
+TEST_F(LibRadosTwoPoolsPP, PromoteSnap) {
+  // create object
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("bar", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("baz", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("bam", &op));
+  }
+
+  // create a snapshot, clone
+  vector<uint64_t> my_snaps(1);
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+                                                        my_snaps));
+  {
+    bufferlist bl;
+    bl.append("ciao!");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("ciao!");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("bar", &op));
+  }
+  {
+    ObjectWriteOperation op;
+    op.remove();
+    ASSERT_EQ(0, ioctx.operate("baz", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("ciao!");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("bam", &op));
+  }
+
+  // configure cache
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name +
+    "\", \"force_nonempty\": \"--force-nonempty\" }",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+    "\", \"mode\": \"writeback\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  // read, trigger a promote on the head
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+    ASSERT_EQ('c', bl[0]);
+  }
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("bam", bl, 1, 0));
+    ASSERT_EQ('c', bl[0]);
+  }
+
+  ioctx.snap_set_read(my_snaps[0]);
+
+  // read foo snap
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+    ASSERT_EQ('h', bl[0]);
+  }
+
+  // read bar snap
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("bar", bl, 1, 0));
+    ASSERT_EQ('h', bl[0]);
+  }
+
+  // read baz snap
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("baz", bl, 1, 0));
+    ASSERT_EQ('h', bl[0]);
+  }
+
+  ioctx.snap_set_read(librados::SNAP_HEAD);
+
+  // read foo
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+    ASSERT_EQ('c', bl[0]);
+  }
+
+  // read bar
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("bar", bl, 1, 0));
+    ASSERT_EQ('c', bl[0]);
+  }
+
+  // read baz
+  {
+    bufferlist bl;
+    ASSERT_EQ(-ENOENT, ioctx.read("baz", bl, 1, 0));
+  }
+
+  // cleanup
+  ioctx.selfmanaged_snap_remove(my_snaps[0]);
+}
+
+TEST_F(LibRadosTwoPoolsPP, PromoteSnapScrub) {
+  int num = 100;
+
+  // create objects
+  for (int i=0; i<num; ++i) {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate(string("foo") + stringify(i), &op));
+  }
+
+  vector<uint64_t> my_snaps;
+  for (int snap=0; snap<4; ++snap) {
+    // create a snapshot, clone
+    vector<uint64_t> ns(1);
+    ns.insert(ns.end(), my_snaps.begin(), my_snaps.end());
+    my_snaps.swap(ns);
+    ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
+    cout << "my_snaps " << my_snaps << std::endl;
+    ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+                                                     my_snaps));
+    for (int i=0; i<num; ++i) {
+      bufferlist bl;
+      bl.append(string("ciao! snap") + stringify(snap));
+      ObjectWriteOperation op;
+      op.write_full(bl);
+      ASSERT_EQ(0, ioctx.operate(string("foo") + stringify(i), &op));
+    }
+  }
+
+  // configure cache
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name +
+    "\", \"force_nonempty\": \"--force-nonempty\" }",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+    "\", \"mode\": \"writeback\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  // read, trigger a promote on _some_ heads to make sure we handle cases
+  // where snaps are present and where they are not.
+  cout << "promoting some heads" << std::endl;
+  for (int i=0; i<num; ++i) {
+    if (i % 5 == 0 || i > num - 3) {
+      bufferlist bl;
+      ASSERT_EQ(1, ioctx.read(string("foo") + stringify(i), bl, 1, 0));
+      ASSERT_EQ('c', bl[0]);
+    }
+  }
+
+  for (unsigned snap = 0; snap < my_snaps.size(); ++snap) {
+    cout << "promoting from clones for snap " << my_snaps[snap] << std::endl;
+    ioctx.snap_set_read(my_snaps[snap]);
+
+    // read some snaps, semi-randomly
+    for (int i=0; i<50; ++i) {
+      bufferlist bl;
+      string o = string("foo") + stringify((snap * i * 137) % 80);
+      //cout << o << std::endl;
+      ASSERT_EQ(1, ioctx.read(o, bl, 1, 0));
+    }
+  }
+
+  // ok, stop and scrub this pool (to make sure scrub can handle
+  // missing clones in the cache tier).
+  {
+    IoCtx cache_ioctx;
+    ASSERT_EQ(0, cluster.ioctx_create(cache_pool_name.c_str(), cache_ioctx));
+    for (int i=0; i<10; ++i) {
+      do {
+       ostringstream ss;
+       ss << "{\"prefix\": \"pg scrub\", \"pgid\": \""
+          << cache_ioctx.get_id() << "." << i
+          << "\"}";
+       int r = cluster.mon_command(ss.str(), inbl, NULL, NULL);
+       if (r == -ENOENT ||  // in case mgr osdmap is stale
+           r == -EAGAIN) {
+         sleep(5);
+         continue;
+       }
+      } while (false);
+    }
+
+    // give it a few seconds to go.  this is sloppy but is usually enough time
+    cout << "waiting for scrubs..." << std::endl;
+    sleep(30);
+    cout << "done waiting" << std::endl;
+  }
+
+  ioctx.snap_set_read(librados::SNAP_HEAD);
+
+  //cleanup
+  for (unsigned snap = 0; snap < my_snaps.size(); ++snap) {
+    ioctx.selfmanaged_snap_remove(my_snaps[snap]);
+  }
+}
+
+TEST_F(LibRadosTwoPoolsPP, PromoteSnapTrimRace) {
+  // create object
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+
+  // create a snapshot, clone
+  vector<uint64_t> my_snaps(1);
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+                                                        my_snaps));
+  {
+    bufferlist bl;
+    bl.append("ciao!");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+
+  // configure cache
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name +
+    "\", \"force_nonempty\": \"--force-nonempty\" }",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+    "\", \"mode\": \"writeback\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  // delete the snap
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_remove(my_snaps[0]));
+
+  ioctx.snap_set_read(my_snaps[0]);
+
+  // read foo snap
+  {
+    bufferlist bl;
+    ASSERT_EQ(-ENOENT, ioctx.read("foo", bl, 1, 0));
+  }
+
+  // cleanup
+  ioctx.selfmanaged_snap_remove(my_snaps[0]);
+}
+
+TEST_F(LibRadosTwoPoolsPP, Whiteout) {
+  // create object
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+
+  // configure cache
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name +
+    "\", \"force_nonempty\": \"--force-nonempty\" }",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+    "\", \"mode\": \"writeback\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  // create some whiteouts, verify they behave
+  {
+    ObjectWriteOperation op;
+    op.assert_exists();
+    op.remove();
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+
+  {
+    ObjectWriteOperation op;
+    op.assert_exists();
+    op.remove();
+    ASSERT_EQ(-ENOENT, ioctx.operate("bar", &op));
+  }
+  {
+    ObjectWriteOperation op;
+    op.assert_exists();
+    op.remove();
+    ASSERT_EQ(-ENOENT, ioctx.operate("bar", &op));
+  }
+
+  // verify the whiteouts are there in the cache tier
+  {
+    NObjectIterator it = cache_ioctx.nobjects_begin();
+    ASSERT_TRUE(it != cache_ioctx.nobjects_end());
+    ASSERT_TRUE(it->get_oid() == string("foo") || it->get_oid() == string("bar"));
+    ++it;
+    ASSERT_TRUE(it->get_oid() == string("foo") || it->get_oid() == string("bar"));
+    ++it;
+    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+  }
+
+  // delete a whiteout and verify it goes away
+  ASSERT_EQ(-ENOENT, ioctx.remove("foo"));
+  {
+    ObjectWriteOperation op;
+    op.remove();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate("bar", completion, &op,
+                                  librados::OPERATION_IGNORE_CACHE));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+
+    NObjectIterator it = cache_ioctx.nobjects_begin();
+    ASSERT_TRUE(it != cache_ioctx.nobjects_end());
+    ASSERT_TRUE(it->get_oid() == string("foo"));
+    ++it;
+    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+  }
+
+  // recreate an object and verify we can read it
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+    ASSERT_EQ('h', bl[0]);
+  }
+}
+
+TEST_F(LibRadosTwoPoolsPP, WhiteoutDeleteCreate) {
+  // configure cache
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name +
+    "\", \"force_nonempty\": \"--force-nonempty\" }",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+    "\", \"mode\": \"writeback\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  // create an object
+  {
+    bufferlist bl;
+    bl.append("foo");
+    ASSERT_EQ(0, ioctx.write_full("foo", bl));
+  }
+
+  // do delete + create operation
+  {
+    ObjectWriteOperation op;
+    op.remove();
+    bufferlist bl;
+    bl.append("bar");
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+
+  // verify it still "exists" (w/ new content)
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+    ASSERT_EQ('b', bl[0]);
+  }
+}
+
+TEST_F(LibRadosTwoPoolsPP, Evict) {
+  // create object
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+
+  // configure cache
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name +
+    "\", \"force_nonempty\": \"--force-nonempty\" }",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+    "\", \"mode\": \"writeback\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  // read, trigger a promote
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+  }
+
+  // read, trigger a whiteout, and a dirty object
+  {
+    bufferlist bl;
+    ASSERT_EQ(-ENOENT, ioctx.read("bar", bl, 1, 0));
+    ASSERT_EQ(-ENOENT, ioctx.read("bar", bl, 1, 0));
+    ASSERT_EQ(0, ioctx.write("bar", bl, bl.length(), 0));
+  }
+
+  // verify the object is present in the cache tier
+  {
+    NObjectIterator it = cache_ioctx.nobjects_begin();
+    ASSERT_TRUE(it != cache_ioctx.nobjects_end());
+    ASSERT_TRUE(it->get_oid() == string("foo") || it->get_oid() == string("bar"));
+    ++it;
+    ASSERT_TRUE(it->get_oid() == string("foo") || it->get_oid() == string("bar"));
+    ++it;
+    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+  }
+
+  // pin
+  {
+    ObjectWriteOperation op;
+    op.cache_pin();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+
+  // evict the pinned object with -EPERM
+  {
+    ObjectReadOperation op;
+    op.cache_evict();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op,
+                                        librados::OPERATION_IGNORE_CACHE,
+                                        NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(-EPERM, completion->get_return_value());
+    completion->release();
+  }
+
+  // unpin
+  {
+    ObjectWriteOperation op;
+    op.cache_unpin();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+
+  // flush
+  {
+    ObjectReadOperation op;
+    op.cache_flush();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate(
+      "foo", completion, &op,
+      librados::OPERATION_IGNORE_OVERLAY, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+
+  // verify clean
+  {
+    bool dirty = false;
+    int r = -1;
+    ObjectReadOperation op;
+    op.is_dirty(&dirty, &r);
+    ASSERT_EQ(0, cache_ioctx.operate("foo", &op, NULL));
+    ASSERT_FALSE(dirty);
+    ASSERT_EQ(0, r);
+  }
+
+  // evict
+  {
+    ObjectReadOperation op;
+    op.cache_evict();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op,
+                                        librados::OPERATION_IGNORE_CACHE,
+                                        NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+  {
+    ObjectReadOperation op;
+    op.cache_evict();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate(
+      "foo", completion, &op,
+      librados::OPERATION_IGNORE_CACHE, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+  {
+    ObjectReadOperation op;
+    op.cache_evict();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate(
+      "bar", completion, &op,
+      librados::OPERATION_IGNORE_CACHE, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(-EBUSY, completion->get_return_value());
+    completion->release();
+  }
+}
+
+TEST_F(LibRadosTwoPoolsPP, EvictSnap) {
+  // create object
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("bar", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("baz", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("bam", &op));
+  }
+
+  // create a snapshot, clone
+  vector<uint64_t> my_snaps(1);
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+                                                        my_snaps));
+  {
+    bufferlist bl;
+    bl.append("ciao!");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("ciao!");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("bar", &op));
+  }
+  {
+    ObjectWriteOperation op;
+    op.remove();
+    ASSERT_EQ(0, ioctx.operate("baz", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("ciao!");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("bam", &op));
+  }
+
+  // configure cache
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name +
+    "\", \"force_nonempty\": \"--force-nonempty\" }",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+    "\", \"mode\": \"writeback\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  // read, trigger a promote on the head
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+    ASSERT_EQ('c', bl[0]);
+  }
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("bam", bl, 1, 0));
+    ASSERT_EQ('c', bl[0]);
+  }
+
+  // evict bam
+  {
+    ObjectReadOperation op;
+    op.cache_evict();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate(
+      "bam", completion, &op,
+      librados::OPERATION_IGNORE_CACHE, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+  {
+    bufferlist bl;
+    ObjectReadOperation op;
+    op.read(1, 0, &bl, NULL);
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate(
+      "bam", completion, &op,
+      librados::OPERATION_IGNORE_CACHE, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(-ENOENT, completion->get_return_value());
+    completion->release();
+  }
+
+  // read foo snap
+  ioctx.snap_set_read(my_snaps[0]);
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+    ASSERT_EQ('h', bl[0]);
+  }
+
+  // evict foo snap
+  {
+    ObjectReadOperation op;
+    op.cache_evict();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate(
+      "foo", completion, &op,
+      librados::OPERATION_IGNORE_CACHE, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+  // snap is gone...
+  {
+    bufferlist bl;
+    ObjectReadOperation op;
+    op.read(1, 0, &bl, NULL);
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate(
+      "foo", completion, &op,
+      librados::OPERATION_IGNORE_CACHE, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(-ENOENT, completion->get_return_value());
+    completion->release();
+  }
+  // head is still there...
+  ioctx.snap_set_read(librados::SNAP_HEAD);
+  {
+    bufferlist bl;
+    ObjectReadOperation op;
+    op.read(1, 0, &bl, NULL);
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate(
+      "foo", completion, &op,
+      librados::OPERATION_IGNORE_CACHE, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+
+  // promote head + snap of bar
+  ioctx.snap_set_read(librados::SNAP_HEAD);
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("bar", bl, 1, 0));
+    ASSERT_EQ('c', bl[0]);
+  }
+  ioctx.snap_set_read(my_snaps[0]);
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("bar", bl, 1, 0));
+    ASSERT_EQ('h', bl[0]);
+  }
+
+  // evict bar head (fail)
+  ioctx.snap_set_read(librados::SNAP_HEAD);
+  {
+    ObjectReadOperation op;
+    op.cache_evict();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate(
+      "bar", completion, &op,
+      librados::OPERATION_IGNORE_CACHE, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(-EBUSY, completion->get_return_value());
+    completion->release();
+  }
+
+  // evict bar snap
+  ioctx.snap_set_read(my_snaps[0]);
+  {
+    ObjectReadOperation op;
+    op.cache_evict();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate(
+      "bar", completion, &op,
+      librados::OPERATION_IGNORE_CACHE, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+  // ...and then head
+  ioctx.snap_set_read(librados::SNAP_HEAD);
+  {
+    bufferlist bl;
+    ObjectReadOperation op;
+    op.read(1, 0, &bl, NULL);
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate(
+      "bar", completion, &op,
+      librados::OPERATION_IGNORE_CACHE, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+  {
+    ObjectReadOperation op;
+    op.cache_evict();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate(
+      "bar", completion, &op,
+      librados::OPERATION_IGNORE_CACHE, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+
+  // cleanup
+  ioctx.selfmanaged_snap_remove(my_snaps[0]);
+}
+
+// this test case reproduces http://tracker.ceph.com/issues/8629
+TEST_F(LibRadosTwoPoolsPP, EvictSnap2) {
+  // create object
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+  // create a snapshot, clone
+  vector<uint64_t> my_snaps(1);
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+                                                        my_snaps));
+  {
+    bufferlist bl;
+    bl.append("ciao!");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+  // configure cache
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name +
+    "\", \"force_nonempty\": \"--force-nonempty\" }",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+    "\", \"mode\": \"writeback\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  // read, trigger a promote on the head
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+    ASSERT_EQ('c', bl[0]);
+  }
+
+  // evict
+  {
+    ObjectReadOperation op;
+    op.cache_evict();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate(
+      "foo", completion, &op,
+      librados::OPERATION_IGNORE_CACHE, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+
+  // verify the snapdir is not present in the cache pool
+  {
+    ObjectReadOperation op;
+    librados::snap_set_t snapset;
+    op.list_snaps(&snapset, NULL);
+    ioctx.snap_set_read(librados::SNAP_DIR);
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate("foo", completion, &op,
+                                  librados::OPERATION_IGNORE_CACHE, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(-ENOENT, completion->get_return_value());
+    completion->release();
+  }
+}
+
+//This test case reproduces http://tracker.ceph.com/issues/17445
+TEST_F(LibRadosTwoPoolsPP, ListSnap){
+  // Create object
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("bar", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("baz", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("bam", &op));
+  }
+
+  // Create a snapshot, clone
+  vector<uint64_t> my_snaps(1);
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+                                                        my_snaps));
+  {
+    bufferlist bl;
+    bl.append("ciao!");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("ciao!");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("bar", &op));
+  }
+  {
+    ObjectWriteOperation op;
+    op.remove();
+    ASSERT_EQ(0, ioctx.operate("baz", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("ciao!");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("bam", &op));
+  }
+
+  // Configure cache
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name +
+    "\", \"force_nonempty\": \"--force-nonempty\" }",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+    "\", \"mode\": \"writeback\"}",
+    inbl, NULL, NULL));
+
+  // Wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  // Read, trigger a promote on the head
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+    ASSERT_EQ('c', bl[0]);
+  }
+
+  // Read foo snap
+  ioctx.snap_set_read(my_snaps[0]);
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+    ASSERT_EQ('h', bl[0]);
+  }
+
+  // Evict foo snap
+  {
+    ObjectReadOperation op;
+    op.cache_evict();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate(
+      "foo", completion, &op,
+      librados::OPERATION_IGNORE_CACHE, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+  // Snap is gone...
+  {
+    bufferlist bl;
+    ObjectReadOperation op;
+    op.read(1, 0, &bl, NULL);
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate(
+      "foo", completion, &op,
+      librados::OPERATION_IGNORE_CACHE, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(-ENOENT, completion->get_return_value());
+    completion->release();
+  }
+
+  // Do list-snaps
+  ioctx.snap_set_read(CEPH_SNAPDIR);
+  {
+    snap_set_t snap_set;
+    int snap_ret;
+    ObjectReadOperation op;
+    op.list_snaps(&snap_set, &snap_ret);
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate(
+      "foo", completion, &op,
+      0, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, snap_ret);
+    ASSERT_LT(0u, snap_set.clones.size());
+    for (vector<librados::clone_info_t>::const_iterator r = snap_set.clones.begin();
+       r != snap_set.clones.end();
+       ++r) {
+      if (r->cloneid != librados::SNAP_HEAD) {
+       ASSERT_LT(0u, r->snaps.size());
+      }
+    }
+  }
+
+  // Cleanup
+  ioctx.selfmanaged_snap_remove(my_snaps[0]);
+}
+
+TEST_F(LibRadosTwoPoolsPP, TryFlush) {
+  // configure cache
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name +
+    "\", \"force_nonempty\": \"--force-nonempty\" }",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+    "\", \"mode\": \"writeback\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  // create object
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+
+  // verify the object is present in the cache tier
+  {
+    NObjectIterator it = cache_ioctx.nobjects_begin();
+    ASSERT_TRUE(it != cache_ioctx.nobjects_end());
+    ASSERT_TRUE(it->get_oid() == string("foo"));
+    ++it;
+    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+  }
+
+  // verify the object is NOT present in the base tier
+  {
+    NObjectIterator it = ioctx.nobjects_begin();
+    ASSERT_TRUE(it == ioctx.nobjects_end());
+  }
+
+  // verify dirty
+  {
+    bool dirty = false;
+    int r = -1;
+    ObjectReadOperation op;
+    op.is_dirty(&dirty, &r);
+    ASSERT_EQ(0, cache_ioctx.operate("foo", &op, NULL));
+    ASSERT_TRUE(dirty);
+    ASSERT_EQ(0, r);
+  }
+
+  // pin
+  {
+    ObjectWriteOperation op;
+    op.cache_pin();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+
+  // flush the pinned object with -EPERM
+  {
+    ObjectReadOperation op;
+    op.cache_try_flush();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate(
+      "foo", completion, &op,
+      librados::OPERATION_IGNORE_OVERLAY |
+      librados::OPERATION_SKIPRWLOCKS, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(-EPERM, completion->get_return_value());
+    completion->release();
+  }
+
+  // unpin
+  {
+    ObjectWriteOperation op;
+    op.cache_unpin();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+
+  // flush
+  {
+    ObjectReadOperation op;
+    op.cache_try_flush();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate(
+      "foo", completion, &op,
+      librados::OPERATION_IGNORE_OVERLAY |
+      librados::OPERATION_SKIPRWLOCKS, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+
+  // verify clean
+  {
+    bool dirty = false;
+    int r = -1;
+    ObjectReadOperation op;
+    op.is_dirty(&dirty, &r);
+    ASSERT_EQ(0, cache_ioctx.operate("foo", &op, NULL));
+    ASSERT_FALSE(dirty);
+    ASSERT_EQ(0, r);
+  }
+
+  // verify in base tier
+  {
+    NObjectIterator it = ioctx.nobjects_begin();
+    ASSERT_TRUE(it != ioctx.nobjects_end());
+    ASSERT_TRUE(it->get_oid() == string("foo"));
+    ++it;
+    ASSERT_TRUE(it == ioctx.nobjects_end());
+  }
+
+  // evict it
+  {
+    ObjectReadOperation op;
+    op.cache_evict();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate(
+        "foo", completion, &op, librados::OPERATION_IGNORE_CACHE, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+
+  // verify no longer in cache tier
+  {
+    NObjectIterator it = cache_ioctx.nobjects_begin();
+    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+  }
+}
+
+TEST_F(LibRadosTwoPoolsPP, Flush) {
+  // configure cache
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name +
+    "\", \"force_nonempty\": \"--force-nonempty\" }",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+    "\", \"mode\": \"writeback\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  uint64_t user_version = 0;
+
+  // create object
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+
+  // verify the object is present in the cache tier
+  {
+    NObjectIterator it = cache_ioctx.nobjects_begin();
+    ASSERT_TRUE(it != cache_ioctx.nobjects_end());
+    ASSERT_TRUE(it->get_oid() == string("foo"));
+    ++it;
+    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+  }
+
+  // verify the object is NOT present in the base tier
+  {
+    NObjectIterator it = ioctx.nobjects_begin();
+    ASSERT_TRUE(it == ioctx.nobjects_end());
+  }
+
+  // verify dirty
+  {
+    bool dirty = false;
+    int r = -1;
+    ObjectReadOperation op;
+    op.is_dirty(&dirty, &r);
+    ASSERT_EQ(0, cache_ioctx.operate("foo", &op, NULL));
+    ASSERT_TRUE(dirty);
+    ASSERT_EQ(0, r);
+    user_version = cache_ioctx.get_last_version();
+  }
+
+  // pin
+  {
+    ObjectWriteOperation op;
+    op.cache_pin();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+
+  // flush the pinned object with -EPERM
+  {
+    ObjectReadOperation op;
+    op.cache_try_flush();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate(
+      "foo", completion, &op,
+      librados::OPERATION_IGNORE_OVERLAY |
+      librados::OPERATION_SKIPRWLOCKS, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(-EPERM, completion->get_return_value());
+    completion->release();
+  }
+
+  // unpin
+  {
+    ObjectWriteOperation op;
+    op.cache_unpin();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+
+  // flush
+  {
+    ObjectReadOperation op;
+    op.cache_flush();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate(
+      "foo", completion, &op,
+      librados::OPERATION_IGNORE_OVERLAY, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+
+  // verify clean
+  {
+    bool dirty = false;
+    int r = -1;
+    ObjectReadOperation op;
+    op.is_dirty(&dirty, &r);
+    ASSERT_EQ(0, cache_ioctx.operate("foo", &op, NULL));
+    ASSERT_FALSE(dirty);
+    ASSERT_EQ(0, r);
+  }
+
+  // verify in base tier
+  {
+    NObjectIterator it = ioctx.nobjects_begin();
+    ASSERT_TRUE(it != ioctx.nobjects_end());
+    ASSERT_TRUE(it->get_oid() == string("foo"));
+    ++it;
+    ASSERT_TRUE(it == ioctx.nobjects_end());
+  }
+
+  // evict it
+  {
+    ObjectReadOperation op;
+    op.cache_evict();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate(
+        "foo", completion, &op, librados::OPERATION_IGNORE_CACHE, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+
+  // verify no longer in cache tier
+  {
+    NObjectIterator it = cache_ioctx.nobjects_begin();
+    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+  }
+
+  // read it again and verify the version is consistent
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, cache_ioctx.read("foo", bl, 1, 0));
+    ASSERT_EQ(user_version, cache_ioctx.get_last_version());
+  }
+
+  // erase it
+  {
+    ObjectWriteOperation op;
+    op.remove();
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+
+  // flush whiteout
+  {
+    ObjectReadOperation op;
+    op.cache_flush();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate(
+      "foo", completion, &op,
+      librados::OPERATION_IGNORE_OVERLAY, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+
+  // evict
+  {
+    ObjectReadOperation op;
+    op.cache_evict();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate(
+        "foo", completion, &op, librados::OPERATION_IGNORE_CACHE, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+
+  // verify no longer in cache tier
+  {
+    NObjectIterator it = cache_ioctx.nobjects_begin();
+    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+  }
+  // or base tier
+  {
+    NObjectIterator it = ioctx.nobjects_begin();
+    ASSERT_TRUE(it == ioctx.nobjects_end());
+  }
+}
+
+TEST_F(LibRadosTwoPoolsPP, FlushSnap) {
+  // configure cache
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name +
+    "\", \"force_nonempty\": \"--force-nonempty\" }",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+    "\", \"mode\": \"writeback\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  // create object
+  {
+    bufferlist bl;
+    bl.append("a");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+
+  // create a snapshot, clone
+  vector<uint64_t> my_snaps(1);
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+                                                        my_snaps));
+  {
+    bufferlist bl;
+    bl.append("b");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+
+  // and another
+  my_snaps.resize(2);
+  my_snaps[1] = my_snaps[0];
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+                                                        my_snaps));
+  {
+    bufferlist bl;
+    bl.append("c");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+
+  // verify the object is present in the cache tier
+  {
+    NObjectIterator it = cache_ioctx.nobjects_begin();
+    ASSERT_TRUE(it != cache_ioctx.nobjects_end());
+    ASSERT_TRUE(it->get_oid() == string("foo"));
+    ++it;
+    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+  }
+
+  // verify the object is NOT present in the base tier
+  {
+    NObjectIterator it = ioctx.nobjects_begin();
+    ASSERT_TRUE(it == ioctx.nobjects_end());
+  }
+
+  // flush on head (should fail)
+  ioctx.snap_set_read(librados::SNAP_HEAD);
+  {
+    ObjectReadOperation op;
+    op.cache_flush();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate(
+      "foo", completion, &op,
+      librados::OPERATION_IGNORE_CACHE, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(-EBUSY, completion->get_return_value());
+    completion->release();
+  }
+  // flush on recent snap (should fail)
+  ioctx.snap_set_read(my_snaps[0]);
+  {
+    ObjectReadOperation op;
+    op.cache_flush();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate(
+      "foo", completion, &op,
+      librados::OPERATION_IGNORE_CACHE, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(-EBUSY, completion->get_return_value());
+    completion->release();
+  }
+  // flush on oldest snap
+  ioctx.snap_set_read(my_snaps[1]);
+  {
+    ObjectReadOperation op;
+    op.cache_flush();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate(
+      "foo", completion, &op,
+      librados::OPERATION_IGNORE_CACHE, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+  // flush on next oldest snap
+  ioctx.snap_set_read(my_snaps[0]);
+  {
+    ObjectReadOperation op;
+    op.cache_flush();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate(
+      "foo", completion, &op,
+      librados::OPERATION_IGNORE_CACHE, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+  // flush on head
+  ioctx.snap_set_read(librados::SNAP_HEAD);
+  {
+    ObjectReadOperation op;
+    op.cache_flush();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate(
+      "foo", completion, &op,
+      librados::OPERATION_IGNORE_CACHE, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+
+  // verify i can read the snaps from the cache pool
+  ioctx.snap_set_read(librados::SNAP_HEAD);
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+    ASSERT_EQ('c', bl[0]);
+  }
+  ioctx.snap_set_read(my_snaps[0]);
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+    ASSERT_EQ('b', bl[0]);
+  }
+  ioctx.snap_set_read(my_snaps[1]);
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+    ASSERT_EQ('a', bl[0]);
+  }
+
+  // remove overlay
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier remove-overlay\", \"pool\": \"" + pool_name +
+    "\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  // verify i can read the snaps from the base pool
+  ioctx.snap_set_read(librados::SNAP_HEAD);
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+    ASSERT_EQ('c', bl[0]);
+  }
+  ioctx.snap_set_read(my_snaps[0]);
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+    ASSERT_EQ('b', bl[0]);
+  }
+  ioctx.snap_set_read(my_snaps[1]);
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+    ASSERT_EQ('a', bl[0]);
+  }
+
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+
+  // cleanup
+  ioctx.selfmanaged_snap_remove(my_snaps[0]);
+}
+
+TEST_F(LibRadosTierPP, FlushWriteRaces) {
+  Rados cluster;
+  std::string pool_name = get_temp_pool_name();
+  std::string cache_pool_name = pool_name + "-cache";
+  ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+  ASSERT_EQ(0, cluster.pool_create(cache_pool_name.c_str()));
+  IoCtx cache_ioctx;
+  ASSERT_EQ(0, cluster.ioctx_create(cache_pool_name.c_str(), cache_ioctx));
+  cache_ioctx.application_enable("rados", true);
+  IoCtx ioctx;
+  ASSERT_EQ(0, cluster.ioctx_create(pool_name.c_str(), ioctx));
+
+  // configure cache
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+    "\", \"mode\": \"writeback\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  // create/dirty object
+  bufferlist bl;
+  bl.append("hi there");
+  {
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+
+  // flush + write
+  {
+    ObjectReadOperation op;
+    op.cache_flush();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate(
+      "foo", completion, &op,
+      librados::OPERATION_IGNORE_OVERLAY, NULL));
+
+    ObjectWriteOperation op2;
+    op2.write_full(bl);
+    librados::AioCompletion *completion2 = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate(
+      "foo", completion2, &op2, 0));
+
+    completion->wait_for_safe();
+    completion2->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    ASSERT_EQ(0, completion2->get_return_value());
+    completion->release();
+    completion2->release();
+  }
+
+  int tries = 1000;
+  do {
+    // create/dirty object
+    {
+      bufferlist bl;
+      bl.append("hi there");
+      ObjectWriteOperation op;
+      op.write_full(bl);
+      ASSERT_EQ(0, ioctx.operate("foo", &op));
+    }
+
+    // try-flush + write
+    {
+      ObjectReadOperation op;
+      op.cache_try_flush();
+      librados::AioCompletion *completion = cluster.aio_create_completion();
+      ASSERT_EQ(0, cache_ioctx.aio_operate(
+        "foo", completion, &op,
+       librados::OPERATION_IGNORE_OVERLAY |
+       librados::OPERATION_SKIPRWLOCKS, NULL));
+
+      ObjectWriteOperation op2;
+      op2.write_full(bl);
+      librados::AioCompletion *completion2 = cluster.aio_create_completion();
+      ASSERT_EQ(0, ioctx.aio_operate("foo", completion2, &op2, 0));
+
+      completion->wait_for_safe();
+      completion2->wait_for_safe();
+      int r = completion->get_return_value();
+      ASSERT_TRUE(r == -EBUSY || r == 0);
+      ASSERT_EQ(0, completion2->get_return_value());
+      completion->release();
+      completion2->release();
+      if (r == -EBUSY)
+       break;
+      cout << "didn't get EBUSY, trying again" << std::endl;
+    }
+    ASSERT_TRUE(--tries);
+  } while (true);
+
+  // tear down tiers
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier remove-overlay\", \"pool\": \"" + pool_name +
+    "\"}",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle before next test
+  cluster.wait_for_latest_osdmap();
+
+  ASSERT_EQ(0, cluster.pool_delete(cache_pool_name.c_str()));
+  ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}
+
+TEST_F(LibRadosTwoPoolsPP, FlushTryFlushRaces) {
+  // configure cache
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name +
+    "\", \"force_nonempty\": \"--force-nonempty\" }",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+    "\", \"mode\": \"writeback\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  // create/dirty object
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+
+  // flush + flush
+  {
+    ObjectReadOperation op;
+    op.cache_flush();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate(
+      "foo", completion, &op,
+      librados::OPERATION_IGNORE_OVERLAY, NULL));
+
+    ObjectReadOperation op2;
+    op2.cache_flush();
+    librados::AioCompletion *completion2 = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate(
+      "foo", completion2, &op2,
+      librados::OPERATION_IGNORE_OVERLAY, NULL));
+
+    completion->wait_for_safe();
+    completion2->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    ASSERT_EQ(0, completion2->get_return_value());
+    completion->release();
+    completion2->release();
+  }
+
+  // create/dirty object
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+
+  // flush + try-flush
+  {
+    ObjectReadOperation op;
+    op.cache_flush();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate(
+      "foo", completion, &op,
+      librados::OPERATION_IGNORE_OVERLAY, NULL));
+
+    ObjectReadOperation op2;
+    op2.cache_try_flush();
+    librados::AioCompletion *completion2 = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate(
+      "foo", completion2, &op2,
+      librados::OPERATION_IGNORE_OVERLAY |
+      librados::OPERATION_SKIPRWLOCKS, NULL));
+
+    completion->wait_for_safe();
+    completion2->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    ASSERT_EQ(0, completion2->get_return_value());
+    completion->release();
+    completion2->release();
+  }
+
+  // create/dirty object
+  int tries = 1000;
+  do {
+    {
+      bufferlist bl;
+      bl.append("hi there");
+      ObjectWriteOperation op;
+      op.write_full(bl);
+      ASSERT_EQ(0, ioctx.operate("foo", &op));
+    }
+
+    // try-flush + flush
+    //  (flush will not piggyback on try-flush)
+    {
+      ObjectReadOperation op;
+      op.cache_try_flush();
+      librados::AioCompletion *completion = cluster.aio_create_completion();
+      ASSERT_EQ(0, cache_ioctx.aio_operate(
+        "foo", completion, &op,
+       librados::OPERATION_IGNORE_OVERLAY |
+       librados::OPERATION_SKIPRWLOCKS, NULL));
+
+      ObjectReadOperation op2;
+      op2.cache_flush();
+      librados::AioCompletion *completion2 = cluster.aio_create_completion();
+      ASSERT_EQ(0, cache_ioctx.aio_operate(
+        "foo", completion2, &op2,
+       librados::OPERATION_IGNORE_OVERLAY, NULL));
+
+      completion->wait_for_safe();
+      completion2->wait_for_safe();
+      int r = completion->get_return_value();
+      ASSERT_TRUE(r == -EBUSY || r == 0);
+      ASSERT_EQ(0, completion2->get_return_value());
+      completion->release();
+      completion2->release();
+      if (r == -EBUSY)
+       break;
+      cout << "didn't get EBUSY, trying again" << std::endl;
+    }
+    ASSERT_TRUE(--tries);
+  } while (true);
+
+  // create/dirty object
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+
+  // try-flush + try-flush
+  {
+    ObjectReadOperation op;
+    op.cache_try_flush();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate(
+      "foo", completion, &op,
+      librados::OPERATION_IGNORE_OVERLAY |
+      librados::OPERATION_SKIPRWLOCKS, NULL));
+
+    ObjectReadOperation op2;
+    op2.cache_try_flush();
+    librados::AioCompletion *completion2 = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate(
+      "foo", completion2, &op2,
+      librados::OPERATION_IGNORE_OVERLAY |
+      librados::OPERATION_SKIPRWLOCKS, NULL));
+
+    completion->wait_for_safe();
+    completion2->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    ASSERT_EQ(0, completion2->get_return_value());
+    completion->release();
+    completion2->release();
+  }
+}
+
+
+IoCtx *read_ioctx = 0;
+Mutex test_lock("FlushReadRaces::lock");
+Cond cond;
+int max_reads = 100;
+int num_reads = 0; // in progress
+
+void flush_read_race_cb(completion_t cb, void *arg);
+
+void start_flush_read()
+{
+  //cout << " starting read" << std::endl;
+  ObjectReadOperation op;
+  op.stat(NULL, NULL, NULL);
+  librados::AioCompletion *completion = completions.getCompletion();
+  completion->set_complete_callback(0, flush_read_race_cb);
+  read_ioctx->aio_operate("foo", completion, &op, NULL);
+}
+
+void flush_read_race_cb(completion_t cb, void *arg)
+{
+  //cout << " finished read" << std::endl;
+  test_lock.Lock();
+  if (num_reads > max_reads) {
+    num_reads--;
+    cond.Signal();
+  } else {
+    start_flush_read();
+  }
+  test_lock.Unlock();
+}
+
+TEST_F(LibRadosTwoPoolsPP, TryFlushReadRace) {
+  // configure cache
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name +
+    "\", \"force_nonempty\": \"--force-nonempty\" }",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+    "\", \"mode\": \"writeback\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  // create/dirty object
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    bufferptr bp(4000000);  // make it big!
+    bp.zero();
+    bl.append(bp);
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+
+  // start a continuous stream of reads
+  read_ioctx = &ioctx;
+  test_lock.Lock();
+  for (int i = 0; i < max_reads; ++i) {
+    start_flush_read();
+    num_reads++;
+  }
+  test_lock.Unlock();
+
+  // try-flush
+  ObjectReadOperation op;
+  op.cache_try_flush();
+  librados::AioCompletion *completion = cluster.aio_create_completion();
+  ASSERT_EQ(0, cache_ioctx.aio_operate(
+      "foo", completion, &op,
+      librados::OPERATION_IGNORE_OVERLAY |
+      librados::OPERATION_SKIPRWLOCKS, NULL));
+
+  completion->wait_for_safe();
+  ASSERT_EQ(0, completion->get_return_value());
+  completion->release();
+
+  // stop reads
+  test_lock.Lock();
+  max_reads = 0;
+  while (num_reads > 0)
+    cond.Wait(test_lock);
+  test_lock.Unlock();
+}
+
+TEST_F(LibRadosTierPP, HitSetNone) {
+  {
+    list< pair<time_t,time_t> > ls;
+    AioCompletion *c = librados::Rados::aio_create_completion();
+    ASSERT_EQ(0, ioctx.hit_set_list(123, c, &ls));
+    c->wait_for_complete();
+    ASSERT_EQ(0, c->get_return_value());
+    ASSERT_TRUE(ls.empty());
+    c->release();
+  }
+  {
+    bufferlist bl;
+    AioCompletion *c = librados::Rados::aio_create_completion();
+    ASSERT_EQ(0, ioctx.hit_set_get(123, c, 12345, &bl));
+    c->wait_for_complete();
+    ASSERT_EQ(-ENOENT, c->get_return_value());
+    c->release();
+  }
+}
+
+string set_pool_str(string pool, string var, string val)
+{
+  return string("{\"prefix\": \"osd pool set\",\"pool\":\"") + pool
+    + string("\",\"var\": \"") + var + string("\",\"val\": \"")
+    + val + string("\"}");
+}
+
+string set_pool_str(string pool, string var, int val)
+{
+  return string("{\"prefix\": \"osd pool set\",\"pool\":\"") + pool
+    + string("\",\"var\": \"") + var + string("\",\"val\": \"")
+    + stringify(val) + string("\"}");
+}
+
+TEST_F(LibRadosTwoPoolsPP, HitSetRead) {
+  // make it a tier
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name +
+    "\", \"force_nonempty\": \"--force-nonempty\" }",
+    inbl, NULL, NULL));
+
+  // enable hitset tracking for this pool
+  ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_count", 2),
+                                               inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_period", 600),
+                                               inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_type",
+                                               "explicit_object"),
+                                  inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  cache_ioctx.set_namespace("");
+
+  // keep reading until we see our object appear in the HitSet
+  utime_t start = ceph_clock_now();
+  utime_t hard_stop = start + utime_t(600, 0);
+
+  while (true) {
+    utime_t now = ceph_clock_now();
+    ASSERT_TRUE(now < hard_stop);
+
+    string name = "foo";
+    uint32_t hash; 
+    ASSERT_EQ(0, cache_ioctx.get_object_hash_position2(name, &hash));
+    hobject_t oid(sobject_t(name, CEPH_NOSNAP), "", hash,
+                 cluster.pool_lookup(cache_pool_name.c_str()), "");
+
+    bufferlist bl;
+    ASSERT_EQ(-ENOENT, cache_ioctx.read("foo", bl, 1, 0));
+
+    bufferlist hbl;
+    AioCompletion *c = librados::Rados::aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.hit_set_get(hash, c, now.sec(), &hbl));
+    c->wait_for_complete();
+    c->release();
+
+    if (hbl.length()) {
+      auto p = hbl.cbegin();
+      HitSet hs;
+      decode(hs, p);
+      if (hs.contains(oid)) {
+       cout << "ok, hit_set contains " << oid << std::endl;
+       break;
+      }
+      cout << "hmm, not in HitSet yet" << std::endl;
+    } else {
+      cout << "hmm, no HitSet yet" << std::endl;
+    }
+
+    sleep(1);
+  }
+}
+
+static int _get_pg_num(Rados& cluster, string pool_name)
+{
+  bufferlist inbl;
+  string cmd = string("{\"prefix\": \"osd pool get\",\"pool\":\"")
+    + pool_name
+    + string("\",\"var\": \"pg_num\",\"format\": \"json\"}");
+  bufferlist outbl;
+  int r = cluster.mon_command(cmd, inbl, &outbl, NULL);
+  ceph_assert(r >= 0);
+  string outstr(outbl.c_str(), outbl.length());
+  json_spirit::Value v;
+  if (!json_spirit::read(outstr, v)) {
+    cerr <<" unable to parse json " << outstr << std::endl;
+    return -1;
+  }
+
+  json_spirit::Object& o = v.get_obj();
+  for (json_spirit::Object::size_type i=0; i<o.size(); i++) {
+    json_spirit::Pair& p = o[i];
+    if (p.name_ == "pg_num") {
+      cout << "pg_num = " << p.value_.get_int() << std::endl;
+      return p.value_.get_int();
+    }
+  }
+  cerr << "didn't find pg_num in " << outstr << std::endl;
+  return -1;
+}
+
+
+TEST_F(LibRadosTwoPoolsPP, HitSetWrite) {
+  int num_pg = _get_pg_num(cluster, pool_name);
+  ceph_assert(num_pg > 0);
+
+  // make it a tier
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name +
+    "\", \"force_nonempty\": \"--force-nonempty\" }",
+    inbl, NULL, NULL));
+
+  // enable hitset tracking for this pool
+  ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_count", 8),
+                                               inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_period", 600),
+                                               inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_type",
+                                               "explicit_hash"),
+                                  inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  cache_ioctx.set_namespace("");
+
+  int num = 200;
+
+  // do a bunch of writes
+  for (int i=0; i<num; ++i) {
+    bufferlist bl;
+    bl.append("a");
+    ASSERT_EQ(0, cache_ioctx.write(stringify(i), bl, 1, 0));
+  }
+
+  // get HitSets
+  std::map<int,HitSet> hitsets;
+  for (int i=0; i<num_pg; ++i) {
+    list< pair<time_t,time_t> > ls;
+    AioCompletion *c = librados::Rados::aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.hit_set_list(i, c, &ls));
+    c->wait_for_complete();
+    c->release();
+    std::cout << "pg " << i << " ls " << ls << std::endl;
+    ASSERT_FALSE(ls.empty());
+
+    // get the latest
+    c = librados::Rados::aio_create_completion();
+    bufferlist bl;
+    ASSERT_EQ(0, cache_ioctx.hit_set_get(i, c, ls.back().first, &bl));
+    c->wait_for_complete();
+    c->release();
+
+    try {
+      auto p = bl.cbegin();
+      decode(hitsets[i], p);
+    }
+    catch (buffer::error& e) {
+      std::cout << "failed to decode hit set; bl len is " << bl.length() << "\n";
+      bl.hexdump(std::cout);
+      std::cout << std::endl;
+      throw e;
+    }
+
+    // cope with racing splits by refreshing pg_num
+    if (i == num_pg - 1)
+      num_pg = _get_pg_num(cluster, cache_pool_name);
+  }
+
+  for (int i=0; i<num; ++i) {
+    string n = stringify(i);
+    uint32_t hash;
+    ASSERT_EQ(0, cache_ioctx.get_object_hash_position2(n, &hash));
+    hobject_t oid(sobject_t(n, CEPH_NOSNAP), "", hash,
+                 cluster.pool_lookup(cache_pool_name.c_str()), "");
+    std::cout << "checking for " << oid << std::endl;
+    bool found = false;
+    for (int p=0; p<num_pg; ++p) {
+      if (hitsets[p].contains(oid)) {
+       found = true;
+       break;
+      }
+    }
+    ASSERT_TRUE(found);
+  }
+}
+
+TEST_F(LibRadosTwoPoolsPP, HitSetTrim) {
+  unsigned count = 3;
+  unsigned period = 3;
+
+  // make it a tier
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name +
+    "\", \"force_nonempty\": \"--force-nonempty\" }",
+    inbl, NULL, NULL));
+
+  // enable hitset tracking for this pool
+  ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_count", count),
+                                               inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_period", period),
+                                               inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_type", "bloom"),
+                                  inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_fpp", ".01"),
+                                  inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  cache_ioctx.set_namespace("");
+
+  // do a bunch of writes and make sure the hitsets rotate
+  utime_t start = ceph_clock_now();
+  utime_t hard_stop = start + utime_t(count * period * 50, 0);
+
+  time_t first = 0;
+  while (true) {
+    string name = "foo";
+    uint32_t hash; 
+    ASSERT_EQ(0, cache_ioctx.get_object_hash_position2(name, &hash));
+    hobject_t oid(sobject_t(name, CEPH_NOSNAP), "", hash, -1, "");
+
+    bufferlist bl;
+    bl.append("f");
+    ASSERT_EQ(0, cache_ioctx.write("foo", bl, 1, 0));
+
+    list<pair<time_t, time_t> > ls;
+    AioCompletion *c = librados::Rados::aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.hit_set_list(hash, c, &ls));
+    c->wait_for_complete();
+    c->release();
+
+    cout << " got ls " << ls << std::endl;
+    if (!ls.empty()) {
+      if (!first) {
+       first = ls.front().first;
+       cout << "first is " << first << std::endl;
+      } else {
+       if (ls.front().first != first) {
+         cout << "first now " << ls.front().first << ", trimmed" << std::endl;
+         break;
+       }
+      }
+    }
+
+    utime_t now = ceph_clock_now();
+    ASSERT_TRUE(now < hard_stop);
+
+    sleep(1);
+  }
+}
+
+TEST_F(LibRadosTwoPoolsPP, PromoteOn2ndRead) {
+  // create object
+  for (int i=0; i<20; ++i) {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo" + stringify(i), &op));
+  }
+
+  // configure cache
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name +
+    "\", \"force_nonempty\": \"--force-nonempty\" }",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+    "\", \"mode\": \"writeback\"}",
+    inbl, NULL, NULL));
+
+  // enable hitset tracking for this pool
+  ASSERT_EQ(0, cluster.mon_command(
+    set_pool_str(cache_pool_name, "hit_set_count", 2),
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    set_pool_str(cache_pool_name, "hit_set_period", 600),
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    set_pool_str(cache_pool_name, "hit_set_type", "bloom"),
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    set_pool_str(cache_pool_name, "min_read_recency_for_promote", 1),
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    set_pool_str(cache_pool_name, "hit_set_grade_decay_rate", 20),
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    set_pool_str(cache_pool_name, "hit_set_search_last_n", 1),
+    inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  int fake = 0;  // set this to non-zero to test spurious promotion,
+                // e.g. from thrashing
+  int attempt = 0;
+  string obj;
+  while (true) {
+    // 1st read, don't trigger a promote
+    obj = "foo" + stringify(attempt);
+    cout << obj << std::endl;
+    {
+      bufferlist bl;
+      ASSERT_EQ(1, ioctx.read(obj.c_str(), bl, 1, 0));
+      if (--fake >= 0) {
+       sleep(1);
+       ASSERT_EQ(1, ioctx.read(obj.c_str(), bl, 1, 0));
+       sleep(1);
+      }
+    }
+
+    // verify the object is NOT present in the cache tier
+    {
+      bool found = false;
+      NObjectIterator it = cache_ioctx.nobjects_begin();
+      while (it != cache_ioctx.nobjects_end()) {
+       cout << " see " << it->get_oid() << std::endl;
+       if (it->get_oid() == string(obj.c_str())) {
+         found = true;
+         break;
+       }
+       ++it;
+      }
+      if (!found)
+       break;
+    }
+
+    ++attempt;
+    ASSERT_LE(attempt, 20);
+    cout << "hrm, object is present in cache on attempt " << attempt
+        << ", retrying" << std::endl;
+  }
+
+  // Read until the object is present in the cache tier
+  cout << "verifying " << obj << " is eventually promoted" << std::endl;
+  while (true) {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read(obj.c_str(), bl, 1, 0));
+
+    bool there = false;
+    NObjectIterator it = cache_ioctx.nobjects_begin();
+    while (it != cache_ioctx.nobjects_end()) {
+      if (it->get_oid() == string(obj.c_str())) {
+       there = true;
+       break;
+      }
+      ++it;
+    }
+    if (there)
+      break;
+
+    sleep(1);
+  }
+
+  // tear down tiers
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier remove-overlay\", \"pool\": \"" + pool_name +
+    "\"}",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle before next test
+  cluster.wait_for_latest_osdmap();
+}
+
+TEST_F(LibRadosTwoPoolsPP, ProxyRead) {
+  // create object
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+
+  // configure cache
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name +
+    "\", \"force_nonempty\": \"--force-nonempty\" }",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+    "\", \"mode\": \"readproxy\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  // read and verify the object
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+    ASSERT_EQ('h', bl[0]);
+  }
+
+  // Verify 10 times the object is NOT present in the cache tier
+  uint32_t i = 0;
+  while (i++ < 10) {
+    NObjectIterator it = cache_ioctx.nobjects_begin();
+    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+    sleep(1);
+  }
+
+  // tear down tiers
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier remove-overlay\", \"pool\": \"" + pool_name +
+    "\"}",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle before next test
+  cluster.wait_for_latest_osdmap();
+}
+
+TEST_F(LibRadosTwoPoolsPP, CachePin) {
+  // create object
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("bar", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("baz", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("bam", &op));
+  }
+
+  // configure cache
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name +
+    "\", \"force_nonempty\": \"--force-nonempty\" }",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+    "\", \"mode\": \"writeback\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  // read, trigger promote
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+    ASSERT_EQ(1, ioctx.read("bar", bl, 1, 0));
+    ASSERT_EQ(1, ioctx.read("baz", bl, 1, 0));
+    ASSERT_EQ(1, ioctx.read("bam", bl, 1, 0));
+  }
+
+  // verify the objects are present in the cache tier
+  {
+    NObjectIterator it = cache_ioctx.nobjects_begin();
+    ASSERT_TRUE(it != cache_ioctx.nobjects_end());
+    for (uint32_t i = 0; i < 4; i++) {
+      ASSERT_TRUE(it->get_oid() == string("foo") ||
+                  it->get_oid() == string("bar") ||
+                  it->get_oid() == string("baz") ||
+                  it->get_oid() == string("bam"));
+      ++it;
+    }
+    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+  }
+
+  // pin objects
+  {
+    ObjectWriteOperation op;
+    op.cache_pin();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+  {
+    ObjectWriteOperation op;
+    op.cache_pin();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate("baz", completion, &op));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+
+  // enable agent
+  ASSERT_EQ(0, cluster.mon_command(
+    set_pool_str(cache_pool_name, "hit_set_count", 2),
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    set_pool_str(cache_pool_name, "hit_set_period", 600),
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    set_pool_str(cache_pool_name, "hit_set_type", "bloom"),
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    set_pool_str(cache_pool_name, "min_read_recency_for_promote", 1),
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    set_pool_str(cache_pool_name, "target_max_objects", 1),
+    inbl, NULL, NULL));
+
+  sleep(10);
+
+  // Verify the pinned object 'foo' is not flushed/evicted
+  uint32_t count = 0;
+  while (true) {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("baz", bl, 1, 0));
+
+    count = 0;
+    NObjectIterator it = cache_ioctx.nobjects_begin();
+    while (it != cache_ioctx.nobjects_end()) {
+      ASSERT_TRUE(it->get_oid() == string("foo") ||
+                  it->get_oid() == string("bar") ||
+                  it->get_oid() == string("baz") ||
+                  it->get_oid() == string("bam"));
+      ++count;
+      ++it;
+    }
+    if (count == 2) {
+      ASSERT_TRUE(it->get_oid() == string("foo") ||
+                  it->get_oid() == string("baz"));
+      break;
+    }
+
+    sleep(1);
+  }
+
+  // tear down tiers
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier remove-overlay\", \"pool\": \"" + pool_name +
+    "\"}",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle before next test
+  cluster.wait_for_latest_osdmap();
+}
+
+TEST_F(LibRadosTwoPoolsPP, SetRedirectRead) {
+  // skip test if not yet luminous
+  {
+    bufferlist inbl, outbl;
+    ASSERT_EQ(0, cluster.mon_command(
+               "{\"prefix\": \"osd dump\"}",
+               inbl, &outbl, NULL));
+    string s(outbl.c_str(), outbl.length());
+    if (s.find("luminous") == std::string::npos) {
+      cout << "cluster is not yet luminous, skipping test" << std::endl;
+      return;
+    }
+  }
+
+  // create object
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, cache_ioctx.operate("bar", &op));
+  }
+
+  // configure tier
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name +
+    "\", \"force_nonempty\": \"--force-nonempty\" }",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  {
+    ObjectWriteOperation op;
+    op.set_redirect("bar", cache_ioctx, 0);
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate("foo", completion, &op));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+  // read and verify the object
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+    ASSERT_EQ('t', bl[0]);
+  }
+
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle before next test
+  cluster.wait_for_latest_osdmap();
+}
+
+TEST_F(LibRadosTwoPoolsPP, SetChunkRead) {
+  // skip test if not yet mimic
+  {
+    bufferlist inbl, outbl;
+    ASSERT_EQ(0, cluster.mon_command(
+               "{\"prefix\": \"osd dump\"}",
+               inbl, &outbl, NULL));
+    string s(outbl.c_str(), outbl.length());
+    if (s.find("mimic") == std::string::npos) {
+      cout << "cluster is not yet mimic, skipping test" << std::endl;
+      return;
+    }
+  }
+
+  // create object
+  {
+    ObjectWriteOperation op;
+    op.create(true);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, cache_ioctx.operate("bar", &op));
+  }
+
+  // configure tier
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name +
+    "\", \"force_nonempty\": \"--force-nonempty\" }",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  // set_chunk
+  {
+    ObjectWriteOperation op;
+    int len = strlen("hi there");
+    for (int i = 0; i < len; i+=2) {
+      op.set_chunk(i, 2, cache_ioctx, "bar", i);
+    }
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate("foo", completion, &op));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+
+  // make all chunks dirty --> full flush --> all chunks are evicted
+  {
+    bufferlist bl;
+    bl.append("There hi");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+
+  // read and verify the object
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+    ASSERT_EQ('T', bl[0]);
+  }
+
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle before next test
+  cluster.wait_for_latest_osdmap();
+}
+
+TEST_F(LibRadosTwoPoolsPP, ManifestPromoteRead) {
+  // skip test if not yet mimic
+  {
+    bufferlist inbl, outbl;
+    ASSERT_EQ(0, cluster.mon_command(
+               "{\"prefix\": \"osd dump\"}",
+               inbl, &outbl, NULL));
+    string s(outbl.c_str(), outbl.length());
+    if (s.find("mimic") == std::string::npos) {
+      cout << "cluster is not yet mimic, skipping test" << std::endl;
+      return;
+    }
+  }
+
+  // create object
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("base chunk");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo-chunk", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, cache_ioctx.operate("bar", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("CHUNK");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, cache_ioctx.operate("bar-chunk", &op));
+  }
+
+  // configure tier
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name +
+    "\", \"force_nonempty\": \"--force-nonempty\" }",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  // set-redirect
+  {
+    ObjectWriteOperation op;
+    op.set_redirect("bar", cache_ioctx, 0);
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate("foo", completion, &op));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+  // set-chunk
+  {
+    ObjectWriteOperation op;
+    op.set_chunk(0, 2, cache_ioctx, "bar-chunk", 0);
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate("foo-chunk", completion, &op));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+  // promote
+  {
+    ObjectWriteOperation op;
+    op.tier_promote();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate("foo", completion, &op));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+  // read and verify the object (redirect)
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+    ASSERT_EQ('t', bl[0]);
+  }
+  // promote
+  {
+    ObjectWriteOperation op;
+    op.tier_promote();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate("foo-chunk", completion, &op));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+  // read and verify the object
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("foo-chunk", bl, 1, 0));
+    ASSERT_EQ('C', bl[0]);
+  }
+
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle before next test
+  cluster.wait_for_latest_osdmap();
+}
+
+TEST_F(LibRadosTwoPoolsPP, ManifestRefRead) {
+  // skip test if not yet mimic
+  {
+    bufferlist inbl, outbl;
+    ASSERT_EQ(0, cluster.mon_command(
+               "{\"prefix\": \"osd dump\"}",
+               inbl, &outbl, NULL));
+    string s(outbl.c_str(), outbl.length());
+    if (s.find("mimic") == std::string::npos) {
+      cout << "cluster is not yet mimic, skipping test" << std::endl;
+      return;
+    }
+  }
+
+  // create object
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("base chunk");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo-chunk", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, cache_ioctx.operate("bar", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("CHUNK");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, cache_ioctx.operate("bar-chunk", &op));
+  }
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  // set-redirect
+  {
+    ObjectWriteOperation op;
+    op.set_redirect("bar", cache_ioctx, 0, CEPH_OSD_OP_FLAG_WITH_REFERENCE);
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate("foo", completion, &op));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+  // set-chunk
+  {
+    ObjectWriteOperation op;
+    op.set_chunk(0, 2, cache_ioctx, "bar-chunk", 0, CEPH_OSD_OP_FLAG_WITH_REFERENCE);
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate("foo-chunk", completion, &op));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+  // redirect's refcount 
+  {
+    bufferlist in, out;
+    cache_ioctx.exec("bar", "cas", "chunk_read", in, out);
+    cls_chunk_refcount_read_ret read_ret;
+    try {
+      auto iter = out.cbegin();
+      decode(read_ret, iter);
+    } catch (buffer::error& err) {
+      ASSERT_TRUE(0);
+    }
+    ASSERT_EQ(1U, read_ret.refs.size());
+  }
+  // chunk's refcount 
+  {
+    bufferlist in, out;
+    cache_ioctx.exec("bar-chunk", "cas", "chunk_read", in, out);
+    cls_chunk_refcount_read_ret read_ret;
+    try {
+      auto iter = out.cbegin();
+      decode(read_ret, iter);
+    } catch (buffer::error& err) {
+      ASSERT_TRUE(0);
+    }
+    ASSERT_EQ(1u, read_ret.refs.size());
+  }
+
+  // wait for maps to settle before next test
+  cluster.wait_for_latest_osdmap();
+}
+
+TEST_F(LibRadosTwoPoolsPP, ManifestUnset) {
+  // skip test if not yet nautilus
+  {
+    bufferlist inbl, outbl;
+    ASSERT_EQ(0, cluster.mon_command(
+               "{\"prefix\": \"osd dump\"}",
+               inbl, &outbl, NULL));
+    string s(outbl.c_str(), outbl.length());
+    if (s.find("nautilus") == std::string::npos) {
+      cout << "cluster is not yet nautilus, skipping test" << std::endl;
+      return;
+    }
+  }
+
+  // create object
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("base chunk");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo-chunk", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, cache_ioctx.operate("bar", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("CHUNK");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, cache_ioctx.operate("bar-chunk", &op));
+  }
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  // set-redirect
+  {
+    ObjectWriteOperation op;
+    op.set_redirect("bar", cache_ioctx, 0, CEPH_OSD_OP_FLAG_WITH_REFERENCE);
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate("foo", completion, &op));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+  // set-chunk
+  {
+    ObjectWriteOperation op;
+    op.set_chunk(0, 2, cache_ioctx, "bar-chunk", 0, CEPH_OSD_OP_FLAG_WITH_REFERENCE);
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate("foo-chunk", completion, &op));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+  // redirect's refcount 
+  {
+    bufferlist in, out;
+    cache_ioctx.exec("bar", "cas", "chunk_read", in, out);
+    cls_chunk_refcount_read_ret read_ret;
+    try {
+      auto iter = out.cbegin();
+      decode(read_ret, iter);
+    } catch (buffer::error& err) {
+      ASSERT_TRUE(0);
+    }
+    ASSERT_EQ(1u, read_ret.refs.size());
+  }
+  // chunk's refcount 
+  {
+    bufferlist in, out;
+    cache_ioctx.exec("bar-chunk", "cas", "chunk_read", in, out);
+    cls_chunk_refcount_read_ret read_ret;
+    try {
+      auto iter = out.cbegin();
+      decode(read_ret, iter);
+    } catch (buffer::error& err) {
+      ASSERT_TRUE(0);
+    }
+    ASSERT_EQ(1u, read_ret.refs.size());
+  }
+
+  // unset-manifest for set-redirect
+  {
+    ObjectWriteOperation op;
+    op.unset_manifest();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate("foo", completion, &op));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+
+  // unset-manifest for set-chunk
+  {
+    ObjectWriteOperation op;
+    op.unset_manifest();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate("foo-chunk", completion, &op));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+  // redirect's refcount 
+  {
+    bufferlist in, out;
+    cache_ioctx.exec("bar", "cas", "chunk_read", in, out);
+    if (out.length() != 0U) {
+      ObjectWriteOperation op;
+      op.unset_manifest();
+      librados::AioCompletion *completion = cluster.aio_create_completion();
+      ASSERT_EQ(0, ioctx.aio_operate("foo", completion, &op));
+      completion->wait_for_safe();
+      ASSERT_EQ(-EOPNOTSUPP, completion->get_return_value());
+      completion->release();
+    }
+  }
+  // chunk's refcount 
+  {
+    bufferlist in, out;
+    cache_ioctx.exec("bar-chunk", "cas", "chunk_read", in, out);
+    if (out.length() != 0U) {
+      ObjectWriteOperation op;
+      op.unset_manifest();
+      librados::AioCompletion *completion = cluster.aio_create_completion();
+      ASSERT_EQ(0, ioctx.aio_operate("foo-chunk", completion, &op));
+      completion->wait_for_safe();
+      ASSERT_EQ(-EOPNOTSUPP, completion->get_return_value());
+      completion->release();
+    }
+  }
+
+  // wait for maps to settle before next test
+  cluster.wait_for_latest_osdmap();
+}
+
+#include "common/ceph_crypto.h"
+using ceph::crypto::SHA1;
+#include "rgw/rgw_common.h"
+TEST_F(LibRadosTwoPoolsPP, ManifestDedupRefRead) {
+  // skip test if not yet nautilus
+  {
+    bufferlist inbl, outbl;
+    ASSERT_EQ(0, cluster.mon_command(
+               "{\"prefix\": \"osd dump\"}",
+               inbl, &outbl, NULL));
+    string s(outbl.c_str(), outbl.length());
+    if (s.find("nautilus") == std::string::npos) {
+      cout << "cluster is not yet nautilus, skipping test" << std::endl;
+      return;
+    }
+  }
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+           set_pool_str(pool_name, "fingerprint_algorithm", "sha1"),
+           inbl, NULL, NULL));
+  cluster.wait_for_latest_osdmap();
+
+  // create object
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo-dedup", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, cache_ioctx.operate("bar", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("CHUNK");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, cache_ioctx.operate("bar-chunk", &op));
+  }
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  // set-chunk (dedup)
+  {
+    ObjectWriteOperation op;
+    int len = strlen("hi there");
+    op.set_chunk(0, len, cache_ioctx, "bar-chunk", 0, 
+               CEPH_OSD_OP_FLAG_WITH_REFERENCE);
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate("foo-dedup", completion, &op));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+  // set-chunk (dedup)
+  {
+    ObjectWriteOperation op;
+    int len = strlen("hi there");
+    op.set_chunk(0, len, cache_ioctx, "bar", 0, 
+               CEPH_OSD_OP_FLAG_WITH_REFERENCE);
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate("foo", completion, &op));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+  // make all chunks dirty --> flush 
+  {
+    // make a dirty chunks
+    bufferlist bl;
+    bl.append("There hi");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo-dedup", &op));
+  }
+  {
+    // do flush
+    bufferlist bl;
+    bl.append("There hi");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo-dedup", &op));
+  }
+  {
+    // make a dirty chunks
+    bufferlist bl;
+    bl.append("There hi");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+  {
+    // do flush
+    bufferlist bl;
+    bl.append("There hi");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+  // chunk's refcount 
+  {
+    bufferlist in, out;
+    SHA1 sha1_gen;
+    int size = strlen("There hi");
+    unsigned char fingerprint[CEPH_CRYPTO_SHA1_DIGESTSIZE + 1];
+    char p_str[CEPH_CRYPTO_SHA1_DIGESTSIZE*2+1] = {0};
+    sha1_gen.Update((const unsigned char *)"There hi", size);
+    sha1_gen.Final(fingerprint);
+    buf_to_hex(fingerprint, CEPH_CRYPTO_SHA1_DIGESTSIZE, p_str);
+    cache_ioctx.exec(p_str, "cas", "chunk_read", in, out);
+    cls_chunk_refcount_read_ret read_ret;
+    try {
+      auto iter = out.cbegin();
+      decode(read_ret, iter);
+    } catch (buffer::error& err) {
+      ASSERT_TRUE(0);
+    }
+    ASSERT_EQ(2u, read_ret.refs.size());
+  }
+
+  // wait for maps to settle before next test
+  cluster.wait_for_latest_osdmap();
+}
+
+class LibRadosTwoPoolsECPP : public RadosTestECPP
+{
+public:
+  LibRadosTwoPoolsECPP() {};
+  ~LibRadosTwoPoolsECPP() override {};
+protected:
+  static void SetUpTestCase() {
+    pool_name = get_temp_pool_name();
+    ASSERT_EQ("", create_one_ec_pool_pp(pool_name, s_cluster));
+  }
+  static void TearDownTestCase() {
+    ASSERT_EQ(0, destroy_one_ec_pool_pp(pool_name, s_cluster));
+  }
+  static std::string cache_pool_name;
+
+  void SetUp() override {
+    cache_pool_name = get_temp_pool_name();
+    ASSERT_EQ(0, s_cluster.pool_create(cache_pool_name.c_str()));
+    RadosTestECPP::SetUp();
+
+    ASSERT_EQ(0, cluster.ioctx_create(cache_pool_name.c_str(), cache_ioctx));
+    cache_ioctx.application_enable("rados", true);
+    cache_ioctx.set_namespace(nspace);
+  }
+  void TearDown() override {
+    // flush + evict cache
+    flush_evict_all(cluster, cache_ioctx);
+
+    bufferlist inbl;
+    // tear down tiers
+    ASSERT_EQ(0, cluster.mon_command(
+      "{\"prefix\": \"osd tier remove-overlay\", \"pool\": \"" + pool_name +
+      "\"}",
+      inbl, NULL, NULL));
+    ASSERT_EQ(0, cluster.mon_command(
+      "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
+      "\", \"tierpool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+
+    // wait for maps to settle before next test
+    cluster.wait_for_latest_osdmap();
+
+    RadosTestECPP::TearDown();
+
+    cleanup_default_namespace(cache_ioctx);
+    cleanup_namespace(cache_ioctx, nspace);
+
+    cache_ioctx.close();
+    ASSERT_EQ(0, s_cluster.pool_delete(cache_pool_name.c_str()));
+  }
+
+  librados::IoCtx cache_ioctx;
+};
+
+std::string LibRadosTwoPoolsECPP::cache_pool_name;
+
+TEST_F(LibRadosTierECPP, Dirty) {
+  {
+    ObjectWriteOperation op;
+    op.undirty();
+    ASSERT_EQ(0, ioctx.operate("foo", &op)); // still get 0 if it dne
+  }
+  {
+    ObjectWriteOperation op;
+    op.create(true);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+  {
+    bool dirty = false;
+    int r = -1;
+    ObjectReadOperation op;
+    op.is_dirty(&dirty, &r);
+    ASSERT_EQ(0, ioctx.operate("foo", &op, NULL));
+    ASSERT_TRUE(dirty);
+    ASSERT_EQ(0, r);
+  }
+  {
+    ObjectWriteOperation op;
+    op.undirty();
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+  {
+    ObjectWriteOperation op;
+    op.undirty();
+    ASSERT_EQ(0, ioctx.operate("foo", &op));  // still 0 if already clean
+  }
+  {
+    bool dirty = false;
+    int r = -1;
+    ObjectReadOperation op;
+    op.is_dirty(&dirty, &r);
+    ASSERT_EQ(0, ioctx.operate("foo", &op, NULL));
+    ASSERT_FALSE(dirty);
+    ASSERT_EQ(0, r);
+  }
+  //{
+  //  ObjectWriteOperation op;
+  //  op.truncate(0);  // still a write even tho it is a no-op
+  //  ASSERT_EQ(0, ioctx.operate("foo", &op));
+  //}
+  //{
+  //  bool dirty = false;
+  //  int r = -1;
+  //  ObjectReadOperation op;
+  //  op.is_dirty(&dirty, &r);
+  //  ASSERT_EQ(0, ioctx.operate("foo", &op, NULL));
+  //  ASSERT_TRUE(dirty);
+  //  ASSERT_EQ(0, r);
+  //}
+}
+
+TEST_F(LibRadosTwoPoolsECPP, Overlay) {
+  // create objects
+  {
+    bufferlist bl;
+    bl.append("base");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("cache");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, cache_ioctx.operate("foo", &op));
+  }
+
+  // configure cache
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name +
+    "\", \"force_nonempty\": \"--force-nonempty\" }",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  // by default, the overlay sends us to cache pool
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+    ASSERT_EQ('c', bl[0]);
+  }
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, cache_ioctx.read("foo", bl, 1, 0));
+    ASSERT_EQ('c', bl[0]);
+  }
+
+  // unless we say otherwise
+  {
+    bufferlist bl;
+    ObjectReadOperation op;
+    op.read(0, 1, &bl, NULL);
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate(
+       "foo", completion, &op,
+       librados::OPERATION_IGNORE_OVERLAY, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+    ASSERT_EQ('b', bl[0]);
+  }
+}
+
+TEST_F(LibRadosTwoPoolsECPP, Promote) {
+  // create object
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+
+  // configure cache
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name +
+    "\", \"force_nonempty\": \"--force-nonempty\" }",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+    "\", \"mode\": \"writeback\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  // read, trigger a promote
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+  }
+
+  // read, trigger a whiteout
+  {
+    bufferlist bl;
+    ASSERT_EQ(-ENOENT, ioctx.read("bar", bl, 1, 0));
+    ASSERT_EQ(-ENOENT, ioctx.read("bar", bl, 1, 0));
+  }
+
+  // verify the object is present in the cache tier
+  {
+    NObjectIterator it = cache_ioctx.nobjects_begin();
+    ASSERT_TRUE(it != cache_ioctx.nobjects_end());
+    ASSERT_TRUE(it->get_oid() == string("foo") || it->get_oid() == string("bar"));
+    ++it;
+    ASSERT_TRUE(it->get_oid() == string("foo") || it->get_oid() == string("bar"));
+    ++it;
+    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+  }
+}
+
+TEST_F(LibRadosTwoPoolsECPP, PromoteSnap) {
+  // create object
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("bar", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("baz", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("bam", &op));
+  }
+
+  // create a snapshot, clone
+  vector<uint64_t> my_snaps(1);
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+                                                        my_snaps));
+  {
+    bufferlist bl;
+    bl.append("ciao!");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("ciao!");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("bar", &op));
+  }
+  {
+    ObjectWriteOperation op;
+    op.remove();
+    ASSERT_EQ(0, ioctx.operate("baz", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("ciao!");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("bam", &op));
+  }
+
+  // configure cache
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name +
+    "\", \"force_nonempty\": \"--force-nonempty\" }",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+    "\", \"mode\": \"writeback\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  // read, trigger a promote on the head
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+    ASSERT_EQ('c', bl[0]);
+  }
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("bam", bl, 1, 0));
+    ASSERT_EQ('c', bl[0]);
+  }
+
+  ioctx.snap_set_read(my_snaps[0]);
+
+  // stop and scrub this pg (to make sure scrub can handle missing
+  // clones in the cache tier)
+  // This test requires cache tier and base tier to have the same pg_num/pgp_num
+  {
+    for (int tries = 0; tries < 5; ++tries) {
+      IoCtx cache_ioctx;
+      ASSERT_EQ(0, cluster.ioctx_create(cache_pool_name.c_str(), cache_ioctx));
+      uint32_t hash;
+      ASSERT_EQ(0, ioctx.get_object_pg_hash_position2("foo", &hash));
+      ostringstream ss;
+      ss << "{\"prefix\": \"pg scrub\", \"pgid\": \""
+        << cache_ioctx.get_id() << "."
+        << hash
+        << "\"}";
+      int r = cluster.mon_command(ss.str(), inbl, NULL, NULL);
+      if (r == -EAGAIN ||
+         r == -ENOENT) {  // in case mgr osdmap is a bit stale
+       sleep(5);
+       continue;
+      }
+      ASSERT_EQ(0, r);
+      break;
+    }
+    // give it a few seconds to go.  this is sloppy but is usually enough time
+    cout << "waiting for scrub..." << std::endl;
+    sleep(15);
+    cout << "done waiting" << std::endl;
+  }
+
+  // read foo snap
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+    ASSERT_EQ('h', bl[0]);
+  }
+
+  // read bar snap
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("bar", bl, 1, 0));
+    ASSERT_EQ('h', bl[0]);
+  }
+
+  // read baz snap
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("baz", bl, 1, 0));
+    ASSERT_EQ('h', bl[0]);
+  }
+
+  ioctx.snap_set_read(librados::SNAP_HEAD);
+
+  // read foo
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+    ASSERT_EQ('c', bl[0]);
+  }
+
+  // read bar
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("bar", bl, 1, 0));
+    ASSERT_EQ('c', bl[0]);
+  }
+
+  // read baz
+  {
+    bufferlist bl;
+    ASSERT_EQ(-ENOENT, ioctx.read("baz", bl, 1, 0));
+  }
+
+  // cleanup
+  ioctx.selfmanaged_snap_remove(my_snaps[0]);
+}
+
+TEST_F(LibRadosTwoPoolsECPP, PromoteSnapTrimRace) {
+  // create object
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+
+  // create a snapshot, clone
+  vector<uint64_t> my_snaps(1);
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+                                                        my_snaps));
+  {
+    bufferlist bl;
+    bl.append("ciao!");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+
+  // configure cache
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name +
+    "\", \"force_nonempty\": \"--force-nonempty\" }",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+    "\", \"mode\": \"writeback\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  // delete the snap
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_remove(my_snaps[0]));
+
+  ioctx.snap_set_read(my_snaps[0]);
+
+  // read foo snap
+  {
+    bufferlist bl;
+    ASSERT_EQ(-ENOENT, ioctx.read("foo", bl, 1, 0));
+  }
+
+  // cleanup
+  ioctx.selfmanaged_snap_remove(my_snaps[0]);
+}
+
+TEST_F(LibRadosTwoPoolsECPP, Whiteout) {
+  // create object
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+
+  // configure cache
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name +
+    "\", \"force_nonempty\": \"--force-nonempty\" }",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+    "\", \"mode\": \"writeback\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  // create some whiteouts, verify they behave
+  {
+    ObjectWriteOperation op;
+    op.assert_exists();
+    op.remove();
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+
+  {
+    ObjectWriteOperation op;
+    op.assert_exists();
+    op.remove();
+    ASSERT_EQ(-ENOENT, ioctx.operate("bar", &op));
+  }
+  {
+    ObjectWriteOperation op;
+    op.assert_exists();
+    op.remove();
+    ASSERT_EQ(-ENOENT, ioctx.operate("bar", &op));
+  }
+
+  // verify the whiteouts are there in the cache tier
+  {
+    NObjectIterator it = cache_ioctx.nobjects_begin();
+    ASSERT_TRUE(it != cache_ioctx.nobjects_end());
+    ASSERT_TRUE(it->get_oid() == string("foo") || it->get_oid() == string("bar"));
+    ++it;
+    ASSERT_TRUE(it->get_oid() == string("foo") || it->get_oid() == string("bar"));
+    ++it;
+    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+  }
+
+  // delete a whiteout and verify it goes away
+  ASSERT_EQ(-ENOENT, ioctx.remove("foo"));
+  {
+    ObjectWriteOperation op;
+    op.remove();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate("bar", completion, &op,
+                                  librados::OPERATION_IGNORE_CACHE));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+
+    NObjectIterator it = cache_ioctx.nobjects_begin();
+    ASSERT_TRUE(it != cache_ioctx.nobjects_end());
+    ASSERT_TRUE(it->get_oid() == string("foo"));
+    ++it;
+    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+  }
+
+  // recreate an object and verify we can read it
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+    ASSERT_EQ('h', bl[0]);
+  }
+}
+
+TEST_F(LibRadosTwoPoolsECPP, Evict) {
+  // create object
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+
+  // configure cache
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name +
+    "\", \"force_nonempty\": \"--force-nonempty\" }",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+    "\", \"mode\": \"writeback\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  // read, trigger a promote
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+  }
+
+  // read, trigger a whiteout, and a dirty object
+  {
+    bufferlist bl;
+    ASSERT_EQ(-ENOENT, ioctx.read("bar", bl, 1, 0));
+    ASSERT_EQ(-ENOENT, ioctx.read("bar", bl, 1, 0));
+    ASSERT_EQ(0, ioctx.write("bar", bl, bl.length(), 0));
+  }
+
+  // verify the object is present in the cache tier
+  {
+    NObjectIterator it = cache_ioctx.nobjects_begin();
+    ASSERT_TRUE(it != cache_ioctx.nobjects_end());
+    ASSERT_TRUE(it->get_oid() == string("foo") || it->get_oid() == string("bar"));
+    ++it;
+    ASSERT_TRUE(it->get_oid() == string("foo") || it->get_oid() == string("bar"));
+    ++it;
+    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+  }
+
+  // pin
+  {
+    ObjectWriteOperation op;
+    op.cache_pin();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+
+  // evict the pinned object with -EPERM
+  {
+    ObjectReadOperation op;
+    op.cache_evict();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op,
+                                        librados::OPERATION_IGNORE_CACHE,
+                                        NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(-EPERM, completion->get_return_value());
+    completion->release();
+  }
+
+  // unpin
+  {
+    ObjectWriteOperation op;
+    op.cache_unpin();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+
+  // flush
+  {
+    ObjectReadOperation op;
+    op.cache_flush();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate(
+      "foo", completion, &op,
+      librados::OPERATION_IGNORE_OVERLAY, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+
+  // verify clean
+  {
+    bool dirty = false;
+    int r = -1;
+    ObjectReadOperation op;
+    op.is_dirty(&dirty, &r);
+    ASSERT_EQ(0, cache_ioctx.operate("foo", &op, NULL));
+    ASSERT_FALSE(dirty);
+    ASSERT_EQ(0, r);
+  }
+
+  // evict
+  {
+    ObjectReadOperation op;
+    op.cache_evict();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op,
+                                        librados::OPERATION_IGNORE_CACHE,
+                                        NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+  {
+    ObjectReadOperation op;
+    op.cache_evict();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate(
+      "foo", completion, &op,
+      librados::OPERATION_IGNORE_CACHE, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+  {
+    ObjectReadOperation op;
+    op.cache_evict();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate(
+      "bar", completion, &op,
+      librados::OPERATION_IGNORE_CACHE, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(-EBUSY, completion->get_return_value());
+    completion->release();
+  }
+}
+
+TEST_F(LibRadosTwoPoolsECPP, EvictSnap) {
+  // create object
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("bar", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("baz", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("bam", &op));
+  }
+
+  // create a snapshot, clone
+  vector<uint64_t> my_snaps(1);
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+                                                        my_snaps));
+  {
+    bufferlist bl;
+    bl.append("ciao!");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("ciao!");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("bar", &op));
+  }
+  {
+    ObjectWriteOperation op;
+    op.remove();
+    ASSERT_EQ(0, ioctx.operate("baz", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("ciao!");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("bam", &op));
+  }
+
+  // configure cache
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name +
+    "\", \"force_nonempty\": \"--force-nonempty\" }",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+    "\", \"mode\": \"writeback\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  // read, trigger a promote on the head
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+    ASSERT_EQ('c', bl[0]);
+  }
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("bam", bl, 1, 0));
+    ASSERT_EQ('c', bl[0]);
+  }
+
+  // evict bam
+  {
+    ObjectReadOperation op;
+    op.cache_evict();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate(
+      "bam", completion, &op,
+      librados::OPERATION_IGNORE_CACHE, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+  {
+    bufferlist bl;
+    ObjectReadOperation op;
+    op.read(1, 0, &bl, NULL);
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate(
+      "bam", completion, &op,
+      librados::OPERATION_IGNORE_CACHE, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(-ENOENT, completion->get_return_value());
+    completion->release();
+  }
+
+  // read foo snap
+  ioctx.snap_set_read(my_snaps[0]);
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+    ASSERT_EQ('h', bl[0]);
+  }
+
+  // evict foo snap
+  {
+    ObjectReadOperation op;
+    op.cache_evict();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate(
+      "foo", completion, &op,
+      librados::OPERATION_IGNORE_CACHE, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+  // snap is gone...
+  {
+    bufferlist bl;
+    ObjectReadOperation op;
+    op.read(1, 0, &bl, NULL);
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate(
+      "foo", completion, &op,
+      librados::OPERATION_IGNORE_CACHE, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(-ENOENT, completion->get_return_value());
+    completion->release();
+  }
+  // head is still there...
+  ioctx.snap_set_read(librados::SNAP_HEAD);
+  {
+    bufferlist bl;
+    ObjectReadOperation op;
+    op.read(1, 0, &bl, NULL);
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate(
+      "foo", completion, &op,
+      librados::OPERATION_IGNORE_CACHE, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+
+  // promote head + snap of bar
+  ioctx.snap_set_read(librados::SNAP_HEAD);
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("bar", bl, 1, 0));
+    ASSERT_EQ('c', bl[0]);
+  }
+  ioctx.snap_set_read(my_snaps[0]);
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("bar", bl, 1, 0));
+    ASSERT_EQ('h', bl[0]);
+  }
+
+  // evict bar head (fail)
+  ioctx.snap_set_read(librados::SNAP_HEAD);
+  {
+    ObjectReadOperation op;
+    op.cache_evict();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate(
+      "bar", completion, &op,
+      librados::OPERATION_IGNORE_CACHE, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(-EBUSY, completion->get_return_value());
+    completion->release();
+  }
+
+  // evict bar snap
+  ioctx.snap_set_read(my_snaps[0]);
+  {
+    ObjectReadOperation op;
+    op.cache_evict();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate(
+      "bar", completion, &op,
+      librados::OPERATION_IGNORE_CACHE, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+  // ...and then head
+  ioctx.snap_set_read(librados::SNAP_HEAD);
+  {
+    bufferlist bl;
+    ObjectReadOperation op;
+    op.read(1, 0, &bl, NULL);
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate(
+      "bar", completion, &op,
+      librados::OPERATION_IGNORE_CACHE, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+  {
+    ObjectReadOperation op;
+    op.cache_evict();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate(
+      "bar", completion, &op,
+      librados::OPERATION_IGNORE_CACHE, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+
+  // cleanup
+  ioctx.selfmanaged_snap_remove(my_snaps[0]);
+}
+
+TEST_F(LibRadosTwoPoolsECPP, TryFlush) {
+  // configure cache
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name +
+    "\", \"force_nonempty\": \"--force-nonempty\" }",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+    "\", \"mode\": \"writeback\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  // create object
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+
+  // verify the object is present in the cache tier
+  {
+    NObjectIterator it = cache_ioctx.nobjects_begin();
+    ASSERT_TRUE(it != cache_ioctx.nobjects_end());
+    ASSERT_TRUE(it->get_oid() == string("foo"));
+    ++it;
+    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+  }
+
+  // verify the object is NOT present in the base tier
+  {
+    NObjectIterator it = ioctx.nobjects_begin();
+    ASSERT_TRUE(it == ioctx.nobjects_end());
+  }
+
+  // verify dirty
+  {
+    bool dirty = false;
+    int r = -1;
+    ObjectReadOperation op;
+    op.is_dirty(&dirty, &r);
+    ASSERT_EQ(0, cache_ioctx.operate("foo", &op, NULL));
+    ASSERT_TRUE(dirty);
+    ASSERT_EQ(0, r);
+  }
+
+  // pin
+  {
+    ObjectWriteOperation op;
+    op.cache_pin();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+
+  // flush the pinned object with -EPERM
+  {
+    ObjectReadOperation op;
+    op.cache_try_flush();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate(
+      "foo", completion, &op,
+      librados::OPERATION_IGNORE_OVERLAY |
+      librados::OPERATION_SKIPRWLOCKS, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(-EPERM, completion->get_return_value());
+    completion->release();
+  }
+
+  // unpin
+  {
+    ObjectWriteOperation op;
+    op.cache_unpin();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+
+  // flush
+  {
+    ObjectReadOperation op;
+    op.cache_try_flush();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate(
+      "foo", completion, &op,
+      librados::OPERATION_IGNORE_OVERLAY |
+      librados::OPERATION_SKIPRWLOCKS, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+
+  // verify clean
+  {
+    bool dirty = false;
+    int r = -1;
+    ObjectReadOperation op;
+    op.is_dirty(&dirty, &r);
+    ASSERT_EQ(0, cache_ioctx.operate("foo", &op, NULL));
+    ASSERT_FALSE(dirty);
+    ASSERT_EQ(0, r);
+  }
+
+  // verify in base tier
+  {
+    NObjectIterator it = ioctx.nobjects_begin();
+    ASSERT_TRUE(it != ioctx.nobjects_end());
+    ASSERT_TRUE(it->get_oid() == string("foo"));
+    ++it;
+    ASSERT_TRUE(it == ioctx.nobjects_end());
+  }
+
+  // evict it
+  {
+    ObjectReadOperation op;
+    op.cache_evict();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate(
+        "foo", completion, &op, librados::OPERATION_IGNORE_CACHE, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+
+  // verify no longer in cache tier
+  {
+    NObjectIterator it = cache_ioctx.nobjects_begin();
+    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+  }
+}
+
+TEST_F(LibRadosTwoPoolsECPP, FailedFlush) {
+  // configure cache
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name +
+    "\", \"force_nonempty\": \"--force-nonempty\" }",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+    "\", \"mode\": \"writeback\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  // create object
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+
+  // verify the object is present in the cache tier
+  {
+    NObjectIterator it = cache_ioctx.nobjects_begin();
+    ASSERT_TRUE(it != cache_ioctx.nobjects_end());
+    ASSERT_TRUE(it->get_oid() == string("foo"));
+    ++it;
+    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+  }
+
+  // verify the object is NOT present in the base tier
+  {
+    NObjectIterator it = ioctx.nobjects_begin();
+    ASSERT_TRUE(it == ioctx.nobjects_end());
+  }
+
+  // set omap
+  {
+    ObjectWriteOperation op;
+    std::map<std::string, bufferlist> omap;
+    omap["somekey"] = bufferlist();
+    op.omap_set(omap);
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+
+  // flush
+  {
+    ObjectReadOperation op;
+    op.cache_flush();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate(
+      "foo", completion, &op,
+      librados::OPERATION_IGNORE_OVERLAY, NULL));
+    completion->wait_for_safe();
+    ASSERT_NE(0, completion->get_return_value());
+    completion->release();
+  }
+
+  // get omap
+  {
+    ObjectReadOperation op;
+    bufferlist bl;
+    int prval = 0;
+    std::set<std::string> keys;
+    keys.insert("somekey");
+    std::map<std::string, bufferlist> map;
+
+    op.omap_get_vals_by_keys(keys, &map, &prval);
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op, &bl));
+    sleep(5);
+    bool completed = completion->is_complete();
+    if( !completed ) {
+      cache_ioctx.aio_cancel(completion); 
+      std::cerr << "Most probably test case will hang here, please reset manually" << std::endl;
+      ASSERT_TRUE(completed); //in fact we are locked forever at test case shutdown unless fix for http://tracker.ceph.com/issues/14511 is applied. Seems there is no workaround for that
+    }
+    completion->release();
+  }
+  // verify still not in base tier
+  {
+    ASSERT_TRUE(ioctx.nobjects_begin() == ioctx.nobjects_end());
+  }
+  // erase it
+  {
+    ObjectWriteOperation op;
+    op.remove();
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+  // flush whiteout
+  {
+    ObjectReadOperation op;
+    op.cache_flush();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate(
+      "foo", completion, &op,
+      librados::OPERATION_IGNORE_OVERLAY, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+  // evict
+  {
+    ObjectReadOperation op;
+    op.cache_evict();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate(
+      "foo", completion, &op, librados::OPERATION_IGNORE_CACHE, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+
+  // verify no longer in cache tier
+  {
+    NObjectIterator it = cache_ioctx.nobjects_begin();
+    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+  }
+  // or base tier
+  {
+    NObjectIterator it = ioctx.nobjects_begin();
+    ASSERT_TRUE(it == ioctx.nobjects_end());
+  }
+}
+
+TEST_F(LibRadosTwoPoolsECPP, Flush) {
+  // configure cache
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name +
+    "\", \"force_nonempty\": \"--force-nonempty\" }",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+    "\", \"mode\": \"writeback\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  uint64_t user_version = 0;
+
+  // create object
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+
+  // verify the object is present in the cache tier
+  {
+    NObjectIterator it = cache_ioctx.nobjects_begin();
+    ASSERT_TRUE(it != cache_ioctx.nobjects_end());
+    ASSERT_TRUE(it->get_oid() == string("foo"));
+    ++it;
+    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+  }
+
+  // verify the object is NOT present in the base tier
+  {
+    NObjectIterator it = ioctx.nobjects_begin();
+    ASSERT_TRUE(it == ioctx.nobjects_end());
+  }
+
+  // verify dirty
+  {
+    bool dirty = false;
+    int r = -1;
+    ObjectReadOperation op;
+    op.is_dirty(&dirty, &r);
+    ASSERT_EQ(0, cache_ioctx.operate("foo", &op, NULL));
+    ASSERT_TRUE(dirty);
+    ASSERT_EQ(0, r);
+    user_version = cache_ioctx.get_last_version();
+  }
+
+  // pin
+  {
+    ObjectWriteOperation op;
+    op.cache_pin();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+
+  // flush the pinned object with -EPERM
+  {
+    ObjectReadOperation op;
+    op.cache_try_flush();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate(
+      "foo", completion, &op,
+      librados::OPERATION_IGNORE_OVERLAY |
+      librados::OPERATION_SKIPRWLOCKS, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(-EPERM, completion->get_return_value());
+    completion->release();
+  }
+
+  // unpin
+  {
+    ObjectWriteOperation op;
+    op.cache_unpin();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+
+  // flush
+  {
+    ObjectReadOperation op;
+    op.cache_flush();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate(
+      "foo", completion, &op,
+      librados::OPERATION_IGNORE_OVERLAY, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+
+  // verify clean
+  {
+    bool dirty = false;
+    int r = -1;
+    ObjectReadOperation op;
+    op.is_dirty(&dirty, &r);
+    ASSERT_EQ(0, cache_ioctx.operate("foo", &op, NULL));
+    ASSERT_FALSE(dirty);
+    ASSERT_EQ(0, r);
+  }
+
+  // verify in base tier
+  {
+    NObjectIterator it = ioctx.nobjects_begin();
+    ASSERT_TRUE(it != ioctx.nobjects_end());
+    ASSERT_TRUE(it->get_oid() == string("foo"));
+    ++it;
+    ASSERT_TRUE(it == ioctx.nobjects_end());
+  }
+
+  // evict it
+  {
+    ObjectReadOperation op;
+    op.cache_evict();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate(
+        "foo", completion, &op, librados::OPERATION_IGNORE_CACHE, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+
+  // verify no longer in cache tier
+  {
+    NObjectIterator it = cache_ioctx.nobjects_begin();
+    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+  }
+
+  // read it again and verify the version is consistent
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, cache_ioctx.read("foo", bl, 1, 0));
+    ASSERT_EQ(user_version, cache_ioctx.get_last_version());
+  }
+
+  // erase it
+  {
+    ObjectWriteOperation op;
+    op.remove();
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+
+  // flush whiteout
+  {
+    ObjectReadOperation op;
+    op.cache_flush();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate(
+      "foo", completion, &op,
+      librados::OPERATION_IGNORE_OVERLAY, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+
+  // evict
+  {
+    ObjectReadOperation op;
+    op.cache_evict();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate(
+        "foo", completion, &op, librados::OPERATION_IGNORE_CACHE, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+
+  // verify no longer in cache tier
+  {
+    NObjectIterator it = cache_ioctx.nobjects_begin();
+    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+  }
+  // or base tier
+  {
+    NObjectIterator it = ioctx.nobjects_begin();
+    ASSERT_TRUE(it == ioctx.nobjects_end());
+  }
+}
+
+TEST_F(LibRadosTwoPoolsECPP, FlushSnap) {
+  // configure cache
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name +
+    "\", \"force_nonempty\": \"--force-nonempty\" }",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+    "\", \"mode\": \"writeback\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  // create object
+  {
+    bufferlist bl;
+    bl.append("a");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+
+  // create a snapshot, clone
+  vector<uint64_t> my_snaps(1);
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+                                                        my_snaps));
+  {
+    bufferlist bl;
+    bl.append("b");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+
+  // and another
+  my_snaps.resize(2);
+  my_snaps[1] = my_snaps[0];
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&my_snaps[0]));
+  ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(my_snaps[0],
+                                                        my_snaps));
+  {
+    bufferlist bl;
+    bl.append("c");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+
+  // verify the object is present in the cache tier
+  {
+    NObjectIterator it = cache_ioctx.nobjects_begin();
+    ASSERT_TRUE(it != cache_ioctx.nobjects_end());
+    ASSERT_TRUE(it->get_oid() == string("foo"));
+    ++it;
+    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+  }
+
+  // verify the object is NOT present in the base tier
+  {
+    NObjectIterator it = ioctx.nobjects_begin();
+    ASSERT_TRUE(it == ioctx.nobjects_end());
+  }
+
+  // flush on head (should fail)
+  ioctx.snap_set_read(librados::SNAP_HEAD);
+  {
+    ObjectReadOperation op;
+    op.cache_flush();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate(
+      "foo", completion, &op,
+      librados::OPERATION_IGNORE_CACHE, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(-EBUSY, completion->get_return_value());
+    completion->release();
+  }
+  // flush on recent snap (should fail)
+  ioctx.snap_set_read(my_snaps[0]);
+  {
+    ObjectReadOperation op;
+    op.cache_flush();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate(
+      "foo", completion, &op,
+      librados::OPERATION_IGNORE_CACHE, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(-EBUSY, completion->get_return_value());
+    completion->release();
+  }
+  // flush on oldest snap
+  ioctx.snap_set_read(my_snaps[1]);
+  {
+    ObjectReadOperation op;
+    op.cache_flush();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate(
+      "foo", completion, &op,
+      librados::OPERATION_IGNORE_CACHE, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+  // flush on next oldest snap
+  ioctx.snap_set_read(my_snaps[0]);
+  {
+    ObjectReadOperation op;
+    op.cache_flush();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate(
+      "foo", completion, &op,
+      librados::OPERATION_IGNORE_CACHE, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+  // flush on head
+  ioctx.snap_set_read(librados::SNAP_HEAD);
+  {
+    ObjectReadOperation op;
+    op.cache_flush();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate(
+      "foo", completion, &op,
+      librados::OPERATION_IGNORE_CACHE, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+
+  // verify i can read the snaps from the cache pool
+  ioctx.snap_set_read(librados::SNAP_HEAD);
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+    ASSERT_EQ('c', bl[0]);
+  }
+  ioctx.snap_set_read(my_snaps[0]);
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+    ASSERT_EQ('b', bl[0]);
+  }
+  ioctx.snap_set_read(my_snaps[1]);
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+    ASSERT_EQ('a', bl[0]);
+  }
+
+  // tear down tiers
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier remove-overlay\", \"pool\": \"" + pool_name +
+    "\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  // verify i can read the snaps from the base pool
+  ioctx.snap_set_read(librados::SNAP_HEAD);
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+    ASSERT_EQ('c', bl[0]);
+  }
+  ioctx.snap_set_read(my_snaps[0]);
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+    ASSERT_EQ('b', bl[0]);
+  }
+  ioctx.snap_set_read(my_snaps[1]);
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+    ASSERT_EQ('a', bl[0]);
+  }
+
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+  cluster.wait_for_latest_osdmap();
+
+  // cleanup
+  ioctx.selfmanaged_snap_remove(my_snaps[0]);
+}
+
+TEST_F(LibRadosTierECPP, FlushWriteRaces) {
+  Rados cluster;
+  std::string pool_name = get_temp_pool_name();
+  std::string cache_pool_name = pool_name + "-cache";
+  ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+  ASSERT_EQ(0, cluster.pool_create(cache_pool_name.c_str()));
+  IoCtx cache_ioctx;
+  ASSERT_EQ(0, cluster.ioctx_create(cache_pool_name.c_str(), cache_ioctx));
+  cache_ioctx.application_enable("rados", true);
+  IoCtx ioctx;
+  ASSERT_EQ(0, cluster.ioctx_create(pool_name.c_str(), ioctx));
+
+  // configure cache
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+    "\", \"mode\": \"writeback\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  // create/dirty object
+  bufferlist bl;
+  bl.append("hi there");
+  {
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+
+  // flush + write
+  {
+    ObjectReadOperation op;
+    op.cache_flush();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate(
+      "foo", completion, &op,
+      librados::OPERATION_IGNORE_OVERLAY, NULL));
+
+    ObjectWriteOperation op2;
+    op2.write_full(bl);
+    librados::AioCompletion *completion2 = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate(
+      "foo", completion2, &op2, 0));
+
+    completion->wait_for_safe();
+    completion2->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    ASSERT_EQ(0, completion2->get_return_value());
+    completion->release();
+    completion2->release();
+  }
+
+  int tries = 1000;
+  do {
+    // create/dirty object
+    {
+      bufferlist bl;
+      bl.append("hi there");
+      ObjectWriteOperation op;
+      op.write_full(bl);
+      ASSERT_EQ(0, ioctx.operate("foo", &op));
+    }
+
+    // try-flush + write
+    {
+      ObjectReadOperation op;
+      op.cache_try_flush();
+      librados::AioCompletion *completion = cluster.aio_create_completion();
+      ASSERT_EQ(0, cache_ioctx.aio_operate(
+        "foo", completion, &op,
+       librados::OPERATION_IGNORE_OVERLAY |
+       librados::OPERATION_SKIPRWLOCKS, NULL));
+
+      ObjectWriteOperation op2;
+      op2.write_full(bl);
+      librados::AioCompletion *completion2 = cluster.aio_create_completion();
+      ASSERT_EQ(0, ioctx.aio_operate("foo", completion2, &op2, 0));
+
+      completion->wait_for_safe();
+      completion2->wait_for_safe();
+      int r = completion->get_return_value();
+      ASSERT_TRUE(r == -EBUSY || r == 0);
+      ASSERT_EQ(0, completion2->get_return_value());
+      completion->release();
+      completion2->release();
+      if (r == -EBUSY)
+       break;
+      cout << "didn't get EBUSY, trying again" << std::endl;
+    }
+    ASSERT_TRUE(--tries);
+  } while (true);
+
+  // tear down tiers
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier remove-overlay\", \"pool\": \"" + pool_name +
+    "\"}",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle before next test
+  cluster.wait_for_latest_osdmap();
+
+  ASSERT_EQ(0, cluster.pool_delete(cache_pool_name.c_str()));
+  ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}
+
+TEST_F(LibRadosTwoPoolsECPP, FlushTryFlushRaces) {
+  // configure cache
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name +
+    "\", \"force_nonempty\": \"--force-nonempty\" }",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+    "\", \"mode\": \"writeback\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  // create/dirty object
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+
+  // flush + flush
+  {
+    ObjectReadOperation op;
+    op.cache_flush();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate(
+      "foo", completion, &op,
+      librados::OPERATION_IGNORE_OVERLAY, NULL));
+
+    ObjectReadOperation op2;
+    op2.cache_flush();
+    librados::AioCompletion *completion2 = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate(
+      "foo", completion2, &op2,
+      librados::OPERATION_IGNORE_OVERLAY, NULL));
+
+    completion->wait_for_safe();
+    completion2->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    ASSERT_EQ(0, completion2->get_return_value());
+    completion->release();
+    completion2->release();
+  }
+
+  // create/dirty object
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+
+  // flush + try-flush
+  {
+    ObjectReadOperation op;
+    op.cache_flush();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate(
+      "foo", completion, &op,
+      librados::OPERATION_IGNORE_OVERLAY, NULL));
+
+    ObjectReadOperation op2;
+    op2.cache_try_flush();
+    librados::AioCompletion *completion2 = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate(
+      "foo", completion2, &op2,
+      librados::OPERATION_IGNORE_OVERLAY |
+      librados::OPERATION_SKIPRWLOCKS, NULL));
+
+    completion->wait_for_safe();
+    completion2->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    ASSERT_EQ(0, completion2->get_return_value());
+    completion->release();
+    completion2->release();
+  }
+
+  // create/dirty object
+  int tries = 1000;
+  do {
+    {
+      bufferlist bl;
+      bl.append("hi there");
+      ObjectWriteOperation op;
+      op.write_full(bl);
+      ASSERT_EQ(0, ioctx.operate("foo", &op));
+    }
+
+    // try-flush + flush
+    //  (flush will not piggyback on try-flush)
+    {
+      ObjectReadOperation op;
+      op.cache_try_flush();
+      librados::AioCompletion *completion = cluster.aio_create_completion();
+      ASSERT_EQ(0, cache_ioctx.aio_operate(
+        "foo", completion, &op,
+       librados::OPERATION_IGNORE_OVERLAY |
+       librados::OPERATION_SKIPRWLOCKS, NULL));
+
+      ObjectReadOperation op2;
+      op2.cache_flush();
+      librados::AioCompletion *completion2 = cluster.aio_create_completion();
+      ASSERT_EQ(0, cache_ioctx.aio_operate(
+        "foo", completion2, &op2,
+       librados::OPERATION_IGNORE_OVERLAY, NULL));
+
+      completion->wait_for_safe();
+      completion2->wait_for_safe();
+      int r = completion->get_return_value();
+      ASSERT_TRUE(r == -EBUSY || r == 0);
+      ASSERT_EQ(0, completion2->get_return_value());
+      completion->release();
+      completion2->release();
+      if (r == -EBUSY)
+       break;
+      cout << "didn't get EBUSY, trying again" << std::endl;
+    }
+    ASSERT_TRUE(--tries);
+  } while (true);
+
+  // create/dirty object
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+
+  // try-flush + try-flush
+  {
+    ObjectReadOperation op;
+    op.cache_try_flush();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate(
+      "foo", completion, &op,
+      librados::OPERATION_IGNORE_OVERLAY |
+      librados::OPERATION_SKIPRWLOCKS, NULL));
+
+    ObjectReadOperation op2;
+    op2.cache_try_flush();
+    librados::AioCompletion *completion2 = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate(
+      "foo", completion2, &op2,
+      librados::OPERATION_IGNORE_OVERLAY |
+      librados::OPERATION_SKIPRWLOCKS, NULL));
+
+    completion->wait_for_safe();
+    completion2->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    ASSERT_EQ(0, completion2->get_return_value());
+    completion->release();
+    completion2->release();
+  }
+}
+
+TEST_F(LibRadosTwoPoolsECPP, TryFlushReadRace) {
+  // configure cache
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name +
+    "\", \"force_nonempty\": \"--force-nonempty\" }",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+    "\", \"mode\": \"writeback\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  // create/dirty object
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    bufferptr bp(4000000);  // make it big!
+    bp.zero();
+    bl.append(bp);
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+
+  // start a continuous stream of reads
+  read_ioctx = &ioctx;
+  test_lock.Lock();
+  for (int i = 0; i < max_reads; ++i) {
+    start_flush_read();
+    num_reads++;
+  }
+  test_lock.Unlock();
+
+  // try-flush
+  ObjectReadOperation op;
+  op.cache_try_flush();
+  librados::AioCompletion *completion = cluster.aio_create_completion();
+  ASSERT_EQ(0, cache_ioctx.aio_operate(
+      "foo", completion, &op,
+      librados::OPERATION_IGNORE_OVERLAY |
+      librados::OPERATION_SKIPRWLOCKS, NULL));
+
+  completion->wait_for_safe();
+  ASSERT_EQ(0, completion->get_return_value());
+  completion->release();
+
+  // stop reads
+  test_lock.Lock();
+  max_reads = 0;
+  while (num_reads > 0)
+    cond.Wait(test_lock);
+  test_lock.Unlock();
+}
+
+TEST_F(LibRadosTierECPP, CallForcesPromote) {
+  Rados cluster;
+  std::string pool_name = get_temp_pool_name();
+  std::string cache_pool_name = pool_name + "-cache";
+  ASSERT_EQ("", create_one_ec_pool_pp(pool_name, cluster));
+  ASSERT_EQ(0, cluster.pool_create(cache_pool_name.c_str()));
+  IoCtx cache_ioctx;
+  ASSERT_EQ(0, cluster.ioctx_create(cache_pool_name.c_str(), cache_ioctx));
+  cache_ioctx.application_enable("rados", true);
+  IoCtx ioctx;
+  ASSERT_EQ(0, cluster.ioctx_create(pool_name.c_str(), ioctx));
+
+  // configure cache
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+    "\", \"mode\": \"writeback\"}",
+    inbl, NULL, NULL));
+
+  // set things up such that the op would normally be proxied
+  ASSERT_EQ(0, cluster.mon_command(
+             set_pool_str(cache_pool_name, "hit_set_count", 2),
+             inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+             set_pool_str(cache_pool_name, "hit_set_period", 600),
+             inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+             set_pool_str(cache_pool_name, "hit_set_type",
+                          "explicit_object"),
+             inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+             set_pool_str(cache_pool_name, "min_read_recency_for_promote",
+                          "4"),
+             inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  // create/dirty object
+  bufferlist bl;
+  bl.append("hi there");
+  {
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+
+  // flush
+  {
+    ObjectReadOperation op;
+    op.cache_flush();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate(
+      "foo", completion, &op,
+      librados::OPERATION_IGNORE_OVERLAY, NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+
+  // evict
+  {
+    ObjectReadOperation op;
+    op.cache_evict();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op,
+                                        librados::OPERATION_IGNORE_CACHE,
+                                        NULL));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+
+  // call
+  {
+    ObjectReadOperation op;
+    bufferlist bl;
+    op.exec("rbd", "get_id", bl);
+    bufferlist out;
+    // should get EIO (not an rbd object), not -EOPNOTSUPP (we didn't promote)
+    ASSERT_EQ(-5, ioctx.operate("foo", &op, &out));
+  }
+
+  // make sure foo is back in the cache tier
+  {
+    NObjectIterator it = cache_ioctx.nobjects_begin();
+    ASSERT_TRUE(it != cache_ioctx.nobjects_end());
+    ASSERT_TRUE(it->get_oid() == string("foo"));
+    ++it;
+    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+  }
+
+  // tear down tiers
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier remove-overlay\", \"pool\": \"" + pool_name +
+    "\"}",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle before next test
+  cluster.wait_for_latest_osdmap();
+
+  ASSERT_EQ(0, cluster.pool_delete(cache_pool_name.c_str()));
+  ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}
+
+TEST_F(LibRadosTierECPP, HitSetNone) {
+  {
+    list< pair<time_t,time_t> > ls;
+    AioCompletion *c = librados::Rados::aio_create_completion();
+    ASSERT_EQ(0, ioctx.hit_set_list(123, c, &ls));
+    c->wait_for_complete();
+    ASSERT_EQ(0, c->get_return_value());
+    ASSERT_TRUE(ls.empty());
+    c->release();
+  }
+  {
+    bufferlist bl;
+    AioCompletion *c = librados::Rados::aio_create_completion();
+    ASSERT_EQ(0, ioctx.hit_set_get(123, c, 12345, &bl));
+    c->wait_for_complete();
+    ASSERT_EQ(-ENOENT, c->get_return_value());
+    c->release();
+  }
+}
+
+TEST_F(LibRadosTwoPoolsECPP, HitSetRead) {
+  // make it a tier
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name +
+    "\", \"force_nonempty\": \"--force-nonempty\" }",
+    inbl, NULL, NULL));
+
+  // enable hitset tracking for this pool
+  ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_count", 2),
+                                               inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_period", 600),
+                                               inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_type",
+                                               "explicit_object"),
+                                  inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  cache_ioctx.set_namespace("");
+
+  // keep reading until we see our object appear in the HitSet
+  utime_t start = ceph_clock_now();
+  utime_t hard_stop = start + utime_t(600, 0);
+
+  while (true) {
+    utime_t now = ceph_clock_now();
+    ASSERT_TRUE(now < hard_stop);
+
+    string name = "foo";
+    uint32_t hash;
+    ASSERT_EQ(0, cache_ioctx.get_object_hash_position2(name, &hash));
+    hobject_t oid(sobject_t(name, CEPH_NOSNAP), "", hash,
+                 cluster.pool_lookup(cache_pool_name.c_str()), "");
+
+    bufferlist bl;
+    ASSERT_EQ(-ENOENT, cache_ioctx.read("foo", bl, 1, 0));
+
+    bufferlist hbl;
+    AioCompletion *c = librados::Rados::aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.hit_set_get(hash, c, now.sec(), &hbl));
+    c->wait_for_complete();
+    c->release();
+
+    if (hbl.length()) {
+      auto p = hbl.cbegin();
+      HitSet hs;
+      decode(hs, p);
+      if (hs.contains(oid)) {
+       cout << "ok, hit_set contains " << oid << std::endl;
+       break;
+      }
+      cout << "hmm, not in HitSet yet" << std::endl;
+    } else {
+      cout << "hmm, no HitSet yet" << std::endl;
+    }
+
+    sleep(1);
+  }
+}
+
+// disable this test until hitset-get reliably works on EC pools
+#if 0
+TEST_F(LibRadosTierECPP, HitSetWrite) {
+  int num_pg = _get_pg_num(cluster, pool_name);
+  ceph_assert(num_pg > 0);
+
+  // enable hitset tracking for this pool
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(set_pool_str(pool_name, "hit_set_count", 8),
+                                               inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(set_pool_str(pool_name, "hit_set_period", 600),
+                                               inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(set_pool_str(pool_name, "hit_set_type",
+                                               "explicit_hash"),
+                                  inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  ioctx.set_namespace("");
+
+  // do a bunch of writes
+  for (int i=0; i<1000; ++i) {
+    bufferlist bl;
+    bl.append("a");
+    ASSERT_EQ(0, ioctx.write(stringify(i), bl, 1, 0));
+  }
+
+  // get HitSets
+  std::map<int,HitSet> hitsets;
+  for (int i=0; i<num_pg; ++i) {
+    list< pair<time_t,time_t> > ls;
+    AioCompletion *c = librados::Rados::aio_create_completion();
+    ASSERT_EQ(0, ioctx.hit_set_list(i, c, &ls));
+    c->wait_for_complete();
+    c->release();
+    std::cout << "pg " << i << " ls " << ls << std::endl;
+    ASSERT_FALSE(ls.empty());
+
+    // get the latest
+    c = librados::Rados::aio_create_completion();
+    bufferlist bl;
+    ASSERT_EQ(0, ioctx.hit_set_get(i, c, ls.back().first, &bl));
+    c->wait_for_complete();
+    c->release();
+
+    //std::cout << "bl len is " << bl.length() << "\n";
+    //bl.hexdump(std::cout);
+    //std::cout << std::endl;
+
+    auto p = bl.cbegin();
+    decode(hitsets[i], p);
+
+    // cope with racing splits by refreshing pg_num
+    if (i == num_pg - 1)
+      num_pg = _get_pg_num(cluster, pool_name);
+  }
+
+  for (int i=0; i<1000; ++i) {
+    string n = stringify(i);
+    uint32_t hash = ioctx.get_object_hash_position(n);
+    hobject_t oid(sobject_t(n, CEPH_NOSNAP), "", hash,
+                 cluster.pool_lookup(pool_name.c_str()), "");
+    std::cout << "checking for " << oid << std::endl;
+    bool found = false;
+    for (int p=0; p<num_pg; ++p) {
+      if (hitsets[p].contains(oid)) {
+       found = true;
+       break;
+      }
+    }
+    ASSERT_TRUE(found);
+  }
+}
+#endif
+
+TEST_F(LibRadosTwoPoolsECPP, HitSetTrim) {
+  unsigned count = 3;
+  unsigned period = 3;
+
+  // make it a tier
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name +
+    "\", \"force_nonempty\": \"--force-nonempty\" }",
+    inbl, NULL, NULL));
+
+  // enable hitset tracking for this pool
+  ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_count", count),
+                                               inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_period", period),
+                                               inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_type", "bloom"),
+                                  inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(set_pool_str(cache_pool_name, "hit_set_fpp", ".01"),
+                                  inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  cache_ioctx.set_namespace("");
+
+  // do a bunch of writes and make sure the hitsets rotate
+  utime_t start = ceph_clock_now();
+  utime_t hard_stop = start + utime_t(count * period * 50, 0);
+
+  time_t first = 0;
+  int bsize = alignment;
+  char *buf = (char *)new char[bsize];
+  memset(buf, 'f', bsize);
+
+  while (true) {
+    string name = "foo";
+    uint32_t hash;
+    ASSERT_EQ(0, cache_ioctx.get_object_hash_position2(name, &hash));
+    hobject_t oid(sobject_t(name, CEPH_NOSNAP), "", hash, -1, "");
+
+    bufferlist bl;
+    bl.append(buf, bsize);
+    ASSERT_EQ(0, cache_ioctx.append("foo", bl, bsize));
+
+    list<pair<time_t, time_t> > ls;
+    AioCompletion *c = librados::Rados::aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.hit_set_list(hash, c, &ls));
+    c->wait_for_complete();
+    c->release();
+
+    cout << " got ls " << ls << std::endl;
+    if (!ls.empty()) {
+      if (!first) {
+       first = ls.front().first;
+       cout << "first is " << first << std::endl;
+      } else {
+       if (ls.front().first != first) {
+         cout << "first now " << ls.front().first << ", trimmed" << std::endl;
+         break;
+       }
+      }
+    }
+
+    utime_t now = ceph_clock_now();
+    ASSERT_TRUE(now < hard_stop);
+
+    sleep(1);
+  }
+  delete[] buf;
+}
+
+TEST_F(LibRadosTwoPoolsECPP, PromoteOn2ndRead) {
+  // create object
+  for (int i=0; i<20; ++i) {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo" + stringify(i), &op));
+  }
+
+  // configure cache
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name +
+    "\", \"force_nonempty\": \"--force-nonempty\" }",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+    "\", \"mode\": \"writeback\"}",
+    inbl, NULL, NULL));
+
+  // enable hitset tracking for this pool
+  ASSERT_EQ(0, cluster.mon_command(
+    set_pool_str(cache_pool_name, "hit_set_count", 2),
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    set_pool_str(cache_pool_name, "hit_set_period", 600),
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    set_pool_str(cache_pool_name, "hit_set_type", "bloom"),
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    set_pool_str(cache_pool_name, "min_read_recency_for_promote", 1),
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    set_pool_str(cache_pool_name, "hit_set_grade_decay_rate", 20),
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    set_pool_str(cache_pool_name, "hit_set_search_last_n", 1),
+    inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  int fake = 0;  // set this to non-zero to test spurious promotion,
+                // e.g. from thrashing
+  int attempt = 0;
+  string obj;
+  while (true) {
+    // 1st read, don't trigger a promote
+    obj = "foo" + stringify(attempt);
+    cout << obj << std::endl;
+    {
+      bufferlist bl;
+      ASSERT_EQ(1, ioctx.read(obj.c_str(), bl, 1, 0));
+      if (--fake >= 0) {
+       sleep(1);
+       ASSERT_EQ(1, ioctx.read(obj.c_str(), bl, 1, 0));
+       sleep(1);
+      }
+    }
+
+    // verify the object is NOT present in the cache tier
+    {
+      bool found = false;
+      NObjectIterator it = cache_ioctx.nobjects_begin();
+      while (it != cache_ioctx.nobjects_end()) {
+       cout << " see " << it->get_oid() << std::endl;
+       if (it->get_oid() == string(obj.c_str())) {
+         found = true;
+         break;
+       }
+       ++it;
+      }
+      if (!found)
+       break;
+    }
+
+    ++attempt;
+    ASSERT_LE(attempt, 20);
+    cout << "hrm, object is present in cache on attempt " << attempt
+        << ", retrying" << std::endl;
+  }
+
+  // Read until the object is present in the cache tier
+  cout << "verifying " << obj << " is eventually promoted" << std::endl;
+  while (true) {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read(obj.c_str(), bl, 1, 0));
+
+    bool there = false;
+    NObjectIterator it = cache_ioctx.nobjects_begin();
+    while (it != cache_ioctx.nobjects_end()) {
+      if (it->get_oid() == string(obj.c_str())) {
+       there = true;
+       break;
+      }
+      ++it;
+    }
+    if (there)
+      break;
+
+    sleep(1);
+  }
+
+  // tear down tiers
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier remove-overlay\", \"pool\": \"" + pool_name +
+    "\"}",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle before next test
+  cluster.wait_for_latest_osdmap();
+}
+
+TEST_F(LibRadosTwoPoolsECPP, ProxyRead) {
+  // create object
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+
+  // configure cache
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name +
+    "\", \"force_nonempty\": \"--force-nonempty\" }",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+    "\", \"mode\": \"readproxy\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  // read and verify the object
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+    ASSERT_EQ('h', bl[0]);
+  }
+
+  // Verify 10 times the object is NOT present in the cache tier
+  uint32_t i = 0;
+  while (i++ < 10) {
+    NObjectIterator it = cache_ioctx.nobjects_begin();
+    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+    sleep(1);
+  }
+
+  // tear down tiers
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier remove-overlay\", \"pool\": \"" + pool_name +
+    "\"}",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle before next test
+  cluster.wait_for_latest_osdmap();
+}
+
+TEST_F(LibRadosTwoPoolsECPP, CachePin) {
+  // create object
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("bar", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("baz", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("bam", &op));
+  }
+
+  // configure cache
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name +
+    "\", \"force_nonempty\": \"--force-nonempty\" }",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+    "\", \"mode\": \"writeback\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  // read, trigger promote
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+    ASSERT_EQ(1, ioctx.read("bar", bl, 1, 0));
+    ASSERT_EQ(1, ioctx.read("baz", bl, 1, 0));
+    ASSERT_EQ(1, ioctx.read("bam", bl, 1, 0));
+  }
+
+  // verify the objects are present in the cache tier
+  {
+    NObjectIterator it = cache_ioctx.nobjects_begin();
+    ASSERT_TRUE(it != cache_ioctx.nobjects_end());
+    for (uint32_t i = 0; i < 4; i++) {
+      ASSERT_TRUE(it->get_oid() == string("foo") ||
+                  it->get_oid() == string("bar") ||
+                  it->get_oid() == string("baz") ||
+                  it->get_oid() == string("bam"));
+      ++it;
+    }
+    ASSERT_TRUE(it == cache_ioctx.nobjects_end());
+  }
+
+  // pin objects
+  {
+    ObjectWriteOperation op;
+    op.cache_pin();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate("foo", completion, &op));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+  {
+    ObjectWriteOperation op;
+    op.cache_pin();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, cache_ioctx.aio_operate("baz", completion, &op));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+
+  // enable agent
+  ASSERT_EQ(0, cluster.mon_command(
+    set_pool_str(cache_pool_name, "hit_set_count", 2),
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    set_pool_str(cache_pool_name, "hit_set_period", 600),
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    set_pool_str(cache_pool_name, "hit_set_type", "bloom"),
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    set_pool_str(cache_pool_name, "min_read_recency_for_promote", 1),
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    set_pool_str(cache_pool_name, "target_max_objects", 1),
+    inbl, NULL, NULL));
+
+  sleep(10);
+
+  // Verify the pinned object 'foo' is not flushed/evicted
+  uint32_t count = 0;
+  while (true) {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("baz", bl, 1, 0));
+
+    count = 0;
+    NObjectIterator it = cache_ioctx.nobjects_begin();
+    while (it != cache_ioctx.nobjects_end()) {
+      ASSERT_TRUE(it->get_oid() == string("foo") ||
+                  it->get_oid() == string("bar") ||
+                  it->get_oid() == string("baz") ||
+                  it->get_oid() == string("bam"));
+      ++count;
+      ++it;
+    }
+    if (count == 2) {
+      ASSERT_TRUE(it->get_oid() == string("foo") ||
+                  it->get_oid() == string("baz"));
+      break;
+    }
+
+    sleep(1);
+  }
+
+  // tear down tiers
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier remove-overlay\", \"pool\": \"" + pool_name +
+    "\"}",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle before next test
+  cluster.wait_for_latest_osdmap();
+}
+TEST_F(LibRadosTwoPoolsECPP, SetRedirectRead) {
+  // skip test if not yet luminous
+  {
+    bufferlist inbl, outbl;
+    ASSERT_EQ(0, cluster.mon_command(
+               "{\"prefix\": \"osd dump\"}",
+               inbl, &outbl, NULL));
+    string s(outbl.c_str(), outbl.length());
+    if (s.find("luminous") == std::string::npos) {
+      cout << "cluster is not yet luminous, skipping test" << std::endl;
+      return;
+    }
+  }
+
+  // create object
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, cache_ioctx.operate("bar", &op));
+  }
+
+  // configure tier
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name +
+    "\", \"force_nonempty\": \"--force-nonempty\" }",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  {
+    ObjectWriteOperation op;
+    op.set_redirect("bar", cache_ioctx, 0);
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate("foo", completion, &op));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+  // read and verify the object
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+    ASSERT_EQ('t', bl[0]);
+  }
+
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle before next test
+  cluster.wait_for_latest_osdmap();
+}
+
+TEST_F(LibRadosTwoPoolsECPP, SetChunkRead) {
+  // skip test if not yet mimic
+  {
+    bufferlist inbl, outbl;
+    ASSERT_EQ(0, cluster.mon_command(
+               "{\"prefix\": \"osd dump\"}",
+               inbl, &outbl, NULL));
+    string s(outbl.c_str(), outbl.length());
+    if (s.find("mimic") == std::string::npos) {
+      cout << "cluster is not yet mimic, skipping test" << std::endl;
+      return;
+    }
+  }
+
+  // create object
+  {
+    ObjectWriteOperation op;
+    op.create(true);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, cache_ioctx.operate("bar", &op));
+  }
+
+  // configure tier
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name +
+    "\", \"force_nonempty\": \"--force-nonempty\" }",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  // set_chunk
+  {
+    ObjectWriteOperation op;
+    op.set_chunk(0, 8, cache_ioctx, "bar", 0);
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate("foo", completion, &op));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+
+  // make all chunks dirty --> full flush --> all chunks are evicted
+  {
+    bufferlist bl;
+    bl.append("There hi");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+
+  // read and verify the object
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+    ASSERT_EQ('T', bl[0]);
+  }
+
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle before next test
+  cluster.wait_for_latest_osdmap();
+}
+
+TEST_F(LibRadosTwoPoolsECPP, ManifestPromoteRead) {
+  // skip test if not yet mimic
+  {
+    bufferlist inbl, outbl;
+    ASSERT_EQ(0, cluster.mon_command(
+               "{\"prefix\": \"osd dump\"}",
+               inbl, &outbl, NULL));
+    string s(outbl.c_str(), outbl.length());
+    if (s.find("mimic") == std::string::npos) {
+      cout << "cluster is not yet mimic, skipping test" << std::endl;
+      return;
+    }
+  }
+
+  // create object
+  {
+    bufferlist bl;
+    bl.append("hi there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, ioctx.operate("foo", &op));
+  }
+  {
+    ObjectWriteOperation op;
+    op.create(true);
+    ASSERT_EQ(0, ioctx.operate("foo-chunk", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("HI there");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, cache_ioctx.operate("bar", &op));
+  }
+  {
+    bufferlist bl;
+    bl.append("BASE CHUNK");
+    ObjectWriteOperation op;
+    op.write_full(bl);
+    ASSERT_EQ(0, cache_ioctx.operate("bar-chunk", &op));
+  }
+
+  // configure tier
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name +
+    "\", \"force_nonempty\": \"--force-nonempty\" }",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  // set-redirect
+  {
+    ObjectWriteOperation op;
+    op.set_redirect("bar", cache_ioctx, 0);
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate("foo", completion, &op));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+  // set-chunk
+  {
+    ObjectWriteOperation op;
+    op.set_chunk(0, 10, cache_ioctx, "bar-chunk", 0);
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate("foo-chunk", completion, &op));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+  // promote
+  {
+    ObjectWriteOperation op;
+    op.tier_promote();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate("foo", completion, &op));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+  // read and verify the object (redirect)
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("foo", bl, 1, 0));
+    ASSERT_EQ('H', bl[0]);
+  }
+  // promote
+  {
+    ObjectWriteOperation op;
+    op.tier_promote();
+    librados::AioCompletion *completion = cluster.aio_create_completion();
+    ASSERT_EQ(0, ioctx.aio_operate("foo-chunk", completion, &op));
+    completion->wait_for_safe();
+    ASSERT_EQ(0, completion->get_return_value());
+    completion->release();
+  }
+  // read and verify the object
+  {
+    bufferlist bl;
+    ASSERT_EQ(1, ioctx.read("foo-chunk", bl, 1, 0));
+    ASSERT_EQ('B', bl[0]);
+  }
+
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier remove\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+
+  // wait for maps to settle before next test
+  cluster.wait_for_latest_osdmap();
+}
+
+TEST_F(LibRadosTwoPoolsPP, PropagateBaseTierError) {
+  // write object  to base tier
+  bufferlist omap_bl;
+  encode(static_cast<uint32_t>(0U), omap_bl);
+
+  ObjectWriteOperation op1;
+  op1.omap_set({{"somekey", omap_bl}});
+  ASSERT_EQ(0, ioctx.operate("propagate-base-tier-error", &op1));
+
+  // configure cache
+  bufferlist inbl;
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier add\", \"pool\": \"" + pool_name +
+    "\", \"tierpool\": \"" + cache_pool_name +
+    "\", \"force_nonempty\": \"--force-nonempty\" }",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + cache_pool_name +
+    "\", \"mode\": \"writeback\"}",
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + pool_name +
+    "\", \"overlaypool\": \"" + cache_pool_name + "\"}",
+    inbl, NULL, NULL));
+
+  ASSERT_EQ(0, cluster.mon_command(
+    set_pool_str(cache_pool_name, "hit_set_type", "bloom"),
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    set_pool_str(cache_pool_name, "hit_set_count", 1),
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    set_pool_str(cache_pool_name, "hit_set_period", 600),
+    inbl, NULL, NULL));
+  ASSERT_EQ(0, cluster.mon_command(
+    set_pool_str(cache_pool_name, "target_max_objects", 250),
+    inbl, NULL, NULL));
+
+  // wait for maps to settle
+  cluster.wait_for_latest_osdmap();
+
+  // guarded op should fail so expect error to propagate to cache tier
+  bufferlist test_omap_bl;
+  encode(static_cast<uint32_t>(1U), test_omap_bl);
+
+  ObjectWriteOperation op2;
+  op2.omap_cmp({{"somekey", {test_omap_bl, CEPH_OSD_CMPXATTR_OP_EQ}}}, nullptr);
+  op2.omap_set({{"somekey", test_omap_bl}});
+
+  ASSERT_EQ(-ECANCELED, ioctx.operate("propagate-base-tier-error", &op2));
+}
index bae3c0852fef5469a26652d1e28142cb40d0bc29..54e54b0803a497be2c714bfdece200b852b2f6e6 100644 (file)
@@ -1,5 +1,4 @@
 #include "include/rados/librados.h"
-#include "include/rados/librados.hpp"
 #include "include/rados/rados_types.h"
 #include "test/librados/test.h"
 #include "test/librados/TestCase.h"
 #include <set>
 #include <map>
 
-using namespace librados;
-
 typedef RadosTestEC LibRadosWatchNotifyEC;
-typedef RadosTestECPP LibRadosWatchNotifyECPP;
 
 int notify_sleep = 0;
 
@@ -28,16 +24,6 @@ static void watch_notify_test_cb(uint8_t opcode, uint64_t ver, void *arg)
   sem_post(sem);
 }
 
-class WatchNotifyTestCtx : public WatchCtx
-{
-public:
-    void notify(uint8_t opcode, uint64_t ver, bufferlist& bl) override
-    {
-      std::cout << __func__ << std::endl;
-      sem_post(sem);
-    }
-};
-
 class LibRadosWatchNotify : public RadosTest
 {
 protected:
@@ -91,49 +77,6 @@ void LibRadosWatchNotify::watch_notify2_test_errcb(void *arg,
 }
 
 class WatchNotifyTestCtx2;
-class LibRadosWatchNotifyPP : public RadosTestParamPP
-{
-protected:
-  bufferlist notify_bl;
-  std::set<uint64_t> notify_cookies;
-  rados_ioctx_t notify_io;
-  const char *notify_oid = nullptr;
-  int notify_err = 0;
-
-  friend class WatchNotifyTestCtx2;
-};
-
-IoCtx *notify_ioctx;
-
-class WatchNotifyTestCtx2 : public WatchCtx2
-{
-  LibRadosWatchNotifyPP *notify;
-
-public:
-  WatchNotifyTestCtx2(LibRadosWatchNotifyPP *notify)
-    : notify(notify)
-  {}
-
-  void handle_notify(uint64_t notify_id, uint64_t cookie, uint64_t notifier_gid,
-                    bufferlist& bl) override {
-    std::cout << __func__ << " cookie " << cookie << " notify_id " << notify_id
-             << " notifier_gid " << notifier_gid << std::endl;
-    notify->notify_bl = bl;
-    notify->notify_cookies.insert(cookie);
-    bufferlist reply;
-    reply.append("reply", 5);
-    if (notify_sleep)
-      sleep(notify_sleep);
-    notify_ioctx->notify_ack(notify->notify_oid, notify_id, cookie, reply);
-  }
-
-  void handle_error(uint64_t cookie, int err) override {
-    std::cout << __func__ << " cookie " << cookie
-             << " err " << err << std::endl;
-    ceph_assert(cookie > 1000);
-    notify->notify_err = err;
-  }
-};
 
 // --
 
@@ -161,27 +104,6 @@ TEST_F(LibRadosWatchNotify, WatchNotify) {
   sem_close(sem);
 }
 
-TEST_P(LibRadosWatchNotifyPP, WatchNotify) {
-  ASSERT_NE(SEM_FAILED, (sem = sem_open("/test_watch_notify_sem", O_CREAT, 0644, 0)));
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
-  uint64_t handle;
-  WatchNotifyTestCtx ctx;
-  ASSERT_EQ(0, ioctx.watch("foo", 0, &handle, &ctx));
-  std::list<obj_watch_t> watches;
-  ASSERT_EQ(0, ioctx.list_watchers("foo", &watches));
-  ASSERT_EQ(1u, watches.size());
-  bufferlist bl2;
-  ASSERT_EQ(0, ioctx.notify("foo", 0, bl2));
-  TestAlarm alarm;
-  sem_wait(sem);
-  ioctx.unwatch("foo", handle);
-  sem_close(sem);
-}
-
 TEST_F(LibRadosWatchNotifyEC, WatchNotify) {
   ASSERT_NE(SEM_FAILED, (sem = sem_open("/test_watch_notify_sem", O_CREAT, 0644, 0)));
   char buf[128];
@@ -197,63 +119,6 @@ TEST_F(LibRadosWatchNotifyEC, WatchNotify) {
   sem_close(sem);
 }
 
-TEST_F(LibRadosWatchNotifyECPP, WatchNotify) {
-  ASSERT_NE(SEM_FAILED, (sem = sem_open("/test_watch_notify_sem", O_CREAT, 0644, 0)));
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
-  uint64_t handle;
-  WatchNotifyTestCtx ctx;
-  ASSERT_EQ(0, ioctx.watch("foo", 0, &handle, &ctx));
-  std::list<obj_watch_t> watches;
-  ASSERT_EQ(0, ioctx.list_watchers("foo", &watches));
-  ASSERT_EQ(1u, watches.size());
-  bufferlist bl2;
-  ASSERT_EQ(0, ioctx.notify("foo", 0, bl2));
-  TestAlarm alarm;
-  sem_wait(sem);
-  ioctx.unwatch("foo", handle);
-  sem_close(sem);
-}
-
-// --
-
-TEST_P(LibRadosWatchNotifyPP, WatchNotifyTimeout) {
-  ASSERT_NE(SEM_FAILED, (sem = sem_open("/test_watch_notify_sem", O_CREAT, 0644, 0)));
-  ioctx.set_notify_timeout(1);
-  uint64_t handle;
-  WatchNotifyTestCtx ctx;
-
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
-
-  ASSERT_EQ(0, ioctx.watch("foo", 0, &handle, &ctx));
-  sem_close(sem);
-  ASSERT_EQ(0, ioctx.unwatch("foo", handle));
-}
-
-TEST_F(LibRadosWatchNotifyECPP, WatchNotifyTimeout) {
-  ASSERT_NE(SEM_FAILED, (sem = sem_open("/test_watch_notify_sem", O_CREAT, 0644, 0)));
-  ioctx.set_notify_timeout(1);
-  uint64_t handle;
-  WatchNotifyTestCtx ctx;
-
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
-
-  ASSERT_EQ(0, ioctx.watch("foo", 0, &handle, &ctx));
-  sem_close(sem);
-  ASSERT_EQ(0, ioctx.unwatch("foo", handle));
-}
-
 #pragma GCC diagnostic pop
 #pragma GCC diagnostic warning "-Wpragmas"
 
@@ -476,121 +341,6 @@ TEST_F(LibRadosWatchNotify, AioNotify) {
   rados_watch_flush(cluster);
 }
 
-TEST_P(LibRadosWatchNotifyPP, WatchNotify2) {
-  notify_oid = "foo";
-  notify_ioctx = &ioctx;
-  notify_cookies.clear();
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.write(notify_oid, bl1, sizeof(buf), 0));
-  uint64_t handle;
-  WatchNotifyTestCtx2 ctx(this);
-  ASSERT_EQ(0, ioctx.watch2(notify_oid, &handle, &ctx));
-  ASSERT_GT(ioctx.watch_check(handle), 0);
-  std::list<obj_watch_t> watches;
-  ASSERT_EQ(0, ioctx.list_watchers(notify_oid, &watches));
-  ASSERT_EQ(watches.size(), 1u);
-  bufferlist bl2, bl_reply;
-  ASSERT_EQ(0, ioctx.notify2(notify_oid, bl2, 300000, &bl_reply));
-  auto p = bl_reply.cbegin();
-  std::map<std::pair<uint64_t,uint64_t>,bufferlist> reply_map;
-  std::set<std::pair<uint64_t,uint64_t> > missed_map;
-  decode(reply_map, p);
-  decode(missed_map, p);
-  ASSERT_EQ(1u, notify_cookies.size());
-  ASSERT_EQ(1u, notify_cookies.count(handle));
-  ASSERT_EQ(1u, reply_map.size());
-  ASSERT_EQ(5u, reply_map.begin()->second.length());
-  ASSERT_EQ(0, strncmp("reply", reply_map.begin()->second.c_str(), 5));
-  ASSERT_EQ(0u, missed_map.size());
-  ASSERT_GT(ioctx.watch_check(handle), 0);
-  ioctx.unwatch2(handle);
-}
-
-TEST_P(LibRadosWatchNotifyPP, AioWatchNotify2) {
-  notify_oid = "foo";
-  notify_ioctx = &ioctx;
-  notify_cookies.clear();
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.write(notify_oid, bl1, sizeof(buf), 0));
-
-  uint64_t handle;
-  WatchNotifyTestCtx2 ctx(this);
-  librados::AioCompletion *comp = cluster.aio_create_completion();
-  ASSERT_EQ(0, ioctx.aio_watch(notify_oid, comp, &handle, &ctx));
-  ASSERT_EQ(0, comp->wait_for_complete());
-  ASSERT_EQ(0, comp->get_return_value());
-  comp->release();
-
-  ASSERT_GT(ioctx.watch_check(handle), 0);
-  std::list<obj_watch_t> watches;
-  ASSERT_EQ(0, ioctx.list_watchers(notify_oid, &watches));
-  ASSERT_EQ(watches.size(), 1u);
-  bufferlist bl2, bl_reply;
-  ASSERT_EQ(0, ioctx.notify2(notify_oid, bl2, 300000, &bl_reply));
-  auto p = bl_reply.cbegin();
-  std::map<std::pair<uint64_t,uint64_t>,bufferlist> reply_map;
-  std::set<std::pair<uint64_t,uint64_t> > missed_map;
-  decode(reply_map, p);
-  decode(missed_map, p);
-  ASSERT_EQ(1u, notify_cookies.size());
-  ASSERT_EQ(1u, notify_cookies.count(handle));
-  ASSERT_EQ(1u, reply_map.size());
-  ASSERT_EQ(5u, reply_map.begin()->second.length());
-  ASSERT_EQ(0, strncmp("reply", reply_map.begin()->second.c_str(), 5));
-  ASSERT_EQ(0u, missed_map.size());
-  ASSERT_GT(ioctx.watch_check(handle), 0);
-
-  comp = cluster.aio_create_completion();
-  ioctx.aio_unwatch(handle, comp);
-  ASSERT_EQ(0, comp->wait_for_complete());
-  comp->release();
-}
-
-
-TEST_P(LibRadosWatchNotifyPP, AioNotify) {
-  notify_oid = "foo";
-  notify_ioctx = &ioctx;
-  notify_cookies.clear();
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.write(notify_oid, bl1, sizeof(buf), 0));
-  uint64_t handle;
-  WatchNotifyTestCtx2 ctx(this);
-  ASSERT_EQ(0, ioctx.watch2(notify_oid, &handle, &ctx));
-  ASSERT_GT(ioctx.watch_check(handle), 0);
-  std::list<obj_watch_t> watches;
-  ASSERT_EQ(0, ioctx.list_watchers(notify_oid, &watches));
-  ASSERT_EQ(watches.size(), 1u);
-  bufferlist bl2, bl_reply;
-  librados::AioCompletion *comp = cluster.aio_create_completion();
-  ASSERT_EQ(0, ioctx.aio_notify(notify_oid, comp, bl2, 300000, &bl_reply));
-  ASSERT_EQ(0, comp->wait_for_complete());
-  ASSERT_EQ(0, comp->get_return_value());
-  comp->release();
-  auto p = bl_reply.cbegin();
-  std::map<std::pair<uint64_t,uint64_t>,bufferlist> reply_map;
-  std::set<std::pair<uint64_t,uint64_t> > missed_map;
-  decode(reply_map, p);
-  decode(missed_map, p);
-  ASSERT_EQ(1u, notify_cookies.size());
-  ASSERT_EQ(1u, notify_cookies.count(handle));
-  ASSERT_EQ(1u, reply_map.size());
-  ASSERT_EQ(5u, reply_map.begin()->second.length());
-  ASSERT_EQ(0, strncmp("reply", reply_map.begin()->second.c_str(), 5));
-  ASSERT_EQ(0u, missed_map.size());
-  ASSERT_GT(ioctx.watch_check(handle), 0);
-  ioctx.unwatch2(handle);
-  cluster.watch_flush();
-}
-
 // --
 
 TEST_F(LibRadosWatchNotify, WatchNotify2Multi) {
@@ -695,88 +445,6 @@ TEST_F(LibRadosWatchNotify, WatchNotify2Timeout) {
 
 }
 
-TEST_P(LibRadosWatchNotifyPP, WatchNotify2Timeout) {
-  notify_oid = "foo";
-  notify_ioctx = &ioctx;
-  notify_sleep = 3;  // 3s
-  notify_cookies.clear();
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.write(notify_oid, bl1, sizeof(buf), 0));
-  uint64_t handle;
-  WatchNotifyTestCtx2 ctx(this);
-  ASSERT_EQ(0, ioctx.watch2(notify_oid, &handle, &ctx));
-  ASSERT_GT(ioctx.watch_check(handle), 0);
-  std::list<obj_watch_t> watches;
-  ASSERT_EQ(0, ioctx.list_watchers(notify_oid, &watches));
-  ASSERT_EQ(watches.size(), 1u);
-  ASSERT_EQ(0u, notify_cookies.size());
-  bufferlist bl2, bl_reply;
-  std::cout << " trying..." << std::endl;
-  ASSERT_EQ(-ETIMEDOUT, ioctx.notify2(notify_oid, bl2, 1000 /* 1s */,
-                                     &bl_reply));
-  std::cout << " timed out" << std::endl;
-  ASSERT_GT(ioctx.watch_check(handle), 0);
-  ioctx.unwatch2(handle);
-
-  std::cout << " flushing" << std::endl;
-  librados::AioCompletion *comp = cluster.aio_create_completion();
-  cluster.aio_watch_flush(comp);
-  ASSERT_EQ(0, comp->wait_for_complete());
-  ASSERT_EQ(0, comp->get_return_value());
-  std::cout << " flushed" << std::endl;
-  comp->release();
-}
-
-TEST_P(LibRadosWatchNotifyPP, WatchNotify3) {
-  notify_oid = "foo";
-  notify_ioctx = &ioctx;
-  notify_cookies.clear();
-  uint32_t timeout = 12; // configured timeout
-  char buf[128];
-  memset(buf, 0xcc, sizeof(buf));
-  bufferlist bl1;
-  bl1.append(buf, sizeof(buf));
-  ASSERT_EQ(0, ioctx.write(notify_oid, bl1, sizeof(buf), 0));
-  uint64_t handle;
-  WatchNotifyTestCtx2 ctx(this);
-  ASSERT_EQ(0, ioctx.watch3(notify_oid, &handle, &ctx, timeout));
-  ASSERT_GT(ioctx.watch_check(handle), 0);
-  std::list<obj_watch_t> watches;
-  ASSERT_EQ(0, ioctx.list_watchers(notify_oid, &watches));
-  ASSERT_EQ(watches.size(), 1u);
-  std::cout << "List watches" << std::endl;
-  for (std::list<obj_watch_t>::iterator it = watches.begin();
-    it != watches.end(); ++it) {
-    ASSERT_EQ(it->timeout_seconds, timeout);
-  }
-  bufferlist bl2, bl_reply;
-  std::cout << "notify2" << std::endl;
-  ASSERT_EQ(0, ioctx.notify2(notify_oid, bl2, 300000, &bl_reply));
-  std::cout << "notify2 done" << std::endl;
-  auto p = bl_reply.cbegin();
-  std::map<std::pair<uint64_t,uint64_t>,bufferlist> reply_map;
-  std::set<std::pair<uint64_t,uint64_t> > missed_map;
-  decode(reply_map, p);
-  decode(missed_map, p);
-  ASSERT_EQ(1u, notify_cookies.size());
-  ASSERT_EQ(1u, notify_cookies.count(handle));
-  ASSERT_EQ(1u, reply_map.size());
-  ASSERT_EQ(5u, reply_map.begin()->second.length());
-  ASSERT_EQ(0, strncmp("reply", reply_map.begin()->second.c_str(), 5));
-  ASSERT_EQ(0u, missed_map.size());
-  std::cout << "watch_check" << std::endl;
-  ASSERT_GT(ioctx.watch_check(handle), 0);
-  std::cout << "unwatch2" << std::endl;
-  ioctx.unwatch2(handle);
-
-  std::cout << " flushing" << std::endl;
-  cluster.watch_flush();
-  std::cout << "done" << std::endl;
-}
-
 TEST_F(LibRadosWatchNotify, Watch3Timeout) {
   notify_io = ioctx;
   notify_oid = "foo";
@@ -913,7 +581,3 @@ TEST_F(LibRadosWatchNotify, AioWatchDelete2) {
   ASSERT_EQ(-ENOENT, rados_aio_get_return_value(comp));
   rados_aio_release(comp);
 }
-// --
-
-INSTANTIATE_TEST_CASE_P(LibRadosWatchNotifyPPTests, LibRadosWatchNotifyPP,
-                       ::testing::Values("", "cache"));
diff --git a/src/test/librados/watch_notify_cxx.cc b/src/test/librados/watch_notify_cxx.cc
new file mode 100644 (file)
index 0000000..0289cba
--- /dev/null
@@ -0,0 +1,364 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <semaphore.h>
+#include <set>
+#include <map>
+
+#include "gtest/gtest.h"
+
+#include "include/encoding.h"
+#include "include/rados/librados.hpp"
+#include "include/rados/rados_types.h"
+#include "test/librados/test_cxx.h"
+#include "test/librados/testcase_cxx.h"
+
+
+using namespace librados;
+
+typedef RadosTestECPP LibRadosWatchNotifyECPP;
+
+int notify_sleep = 0;
+
+#pragma GCC diagnostic ignored "-Wpragmas"
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+
+class LibRadosWatchNotifyPP : public RadosTestParamPP
+{
+protected:
+  bufferlist notify_bl;
+  std::set<uint64_t> notify_cookies;
+  rados_ioctx_t notify_io;
+  const char *notify_oid = nullptr;
+  int notify_err = 0;
+
+  friend class WatchNotifyTestCtx2;
+};
+
+IoCtx *notify_ioctx;
+
+class WatchNotifyTestCtx2 : public WatchCtx2
+{
+  LibRadosWatchNotifyPP *notify;
+
+public:
+  WatchNotifyTestCtx2(LibRadosWatchNotifyPP *notify)
+    : notify(notify)
+  {}
+
+  void handle_notify(uint64_t notify_id, uint64_t cookie, uint64_t notifier_gid,
+                    bufferlist& bl) override {
+    std::cout << __func__ << " cookie " << cookie << " notify_id " << notify_id
+             << " notifier_gid " << notifier_gid << std::endl;
+    notify->notify_bl = bl;
+    notify->notify_cookies.insert(cookie);
+    bufferlist reply;
+    reply.append("reply", 5);
+    if (notify_sleep)
+      sleep(notify_sleep);
+    notify_ioctx->notify_ack(notify->notify_oid, notify_id, cookie, reply);
+  }
+
+  void handle_error(uint64_t cookie, int err) override {
+    std::cout << __func__ << " cookie " << cookie
+             << " err " << err << std::endl;
+    ceph_assert(cookie > 1000);
+    notify->notify_err = err;
+  }
+};
+
+// notify
+static sem_t *sem;
+
+class WatchNotifyTestCtx : public WatchCtx
+{
+public:
+  void notify(uint8_t opcode, uint64_t ver, bufferlist& bl) override
+  {
+    std::cout << __func__ << std::endl;
+    sem_post(sem);
+  }
+};
+
+TEST_P(LibRadosWatchNotifyPP, WatchNotify) {
+  ASSERT_NE(SEM_FAILED, (sem = sem_open("/test_watch_notify_sem", O_CREAT, 0644, 0)));
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+  uint64_t handle;
+  WatchNotifyTestCtx ctx;
+  ASSERT_EQ(0, ioctx.watch("foo", 0, &handle, &ctx));
+  std::list<obj_watch_t> watches;
+  ASSERT_EQ(0, ioctx.list_watchers("foo", &watches));
+  ASSERT_EQ(1u, watches.size());
+  bufferlist bl2;
+  ASSERT_EQ(0, ioctx.notify("foo", 0, bl2));
+  TestAlarm alarm;
+  sem_wait(sem);
+  ioctx.unwatch("foo", handle);
+  sem_close(sem);
+}
+
+TEST_F(LibRadosWatchNotifyECPP, WatchNotify) {
+  ASSERT_NE(SEM_FAILED, (sem = sem_open("/test_watch_notify_sem", O_CREAT, 0644, 0)));
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+  uint64_t handle;
+  WatchNotifyTestCtx ctx;
+  ASSERT_EQ(0, ioctx.watch("foo", 0, &handle, &ctx));
+  std::list<obj_watch_t> watches;
+  ASSERT_EQ(0, ioctx.list_watchers("foo", &watches));
+  ASSERT_EQ(1u, watches.size());
+  bufferlist bl2;
+  ASSERT_EQ(0, ioctx.notify("foo", 0, bl2));
+  TestAlarm alarm;
+  sem_wait(sem);
+  ioctx.unwatch("foo", handle);
+  sem_close(sem);
+}
+
+// --
+
+TEST_P(LibRadosWatchNotifyPP, WatchNotifyTimeout) {
+  ASSERT_NE(SEM_FAILED, (sem = sem_open("/test_watch_notify_sem", O_CREAT, 0644, 0)));
+  ioctx.set_notify_timeout(1);
+  uint64_t handle;
+  WatchNotifyTestCtx ctx;
+
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+
+  ASSERT_EQ(0, ioctx.watch("foo", 0, &handle, &ctx));
+  sem_close(sem);
+  ASSERT_EQ(0, ioctx.unwatch("foo", handle));
+}
+
+TEST_F(LibRadosWatchNotifyECPP, WatchNotifyTimeout) {
+  ASSERT_NE(SEM_FAILED, (sem = sem_open("/test_watch_notify_sem", O_CREAT, 0644, 0)));
+  ioctx.set_notify_timeout(1);
+  uint64_t handle;
+  WatchNotifyTestCtx ctx;
+
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
+
+  ASSERT_EQ(0, ioctx.watch("foo", 0, &handle, &ctx));
+  sem_close(sem);
+  ASSERT_EQ(0, ioctx.unwatch("foo", handle));
+}
+
+#pragma GCC diagnostic pop
+#pragma GCC diagnostic warning "-Wpragmas"
+
+TEST_P(LibRadosWatchNotifyPP, WatchNotify2) {
+  notify_oid = "foo";
+  notify_ioctx = &ioctx;
+  notify_cookies.clear();
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.write(notify_oid, bl1, sizeof(buf), 0));
+  uint64_t handle;
+  WatchNotifyTestCtx2 ctx(this);
+  ASSERT_EQ(0, ioctx.watch2(notify_oid, &handle, &ctx));
+  ASSERT_GT(ioctx.watch_check(handle), 0);
+  std::list<obj_watch_t> watches;
+  ASSERT_EQ(0, ioctx.list_watchers(notify_oid, &watches));
+  ASSERT_EQ(watches.size(), 1u);
+  bufferlist bl2, bl_reply;
+  ASSERT_EQ(0, ioctx.notify2(notify_oid, bl2, 300000, &bl_reply));
+  auto p = bl_reply.cbegin();
+  std::map<std::pair<uint64_t,uint64_t>,bufferlist> reply_map;
+  std::set<std::pair<uint64_t,uint64_t> > missed_map;
+  decode(reply_map, p);
+  decode(missed_map, p);
+  ASSERT_EQ(1u, notify_cookies.size());
+  ASSERT_EQ(1u, notify_cookies.count(handle));
+  ASSERT_EQ(1u, reply_map.size());
+  ASSERT_EQ(5u, reply_map.begin()->second.length());
+  ASSERT_EQ(0, strncmp("reply", reply_map.begin()->second.c_str(), 5));
+  ASSERT_EQ(0u, missed_map.size());
+  ASSERT_GT(ioctx.watch_check(handle), 0);
+  ioctx.unwatch2(handle);
+}
+
+TEST_P(LibRadosWatchNotifyPP, AioWatchNotify2) {
+  notify_oid = "foo";
+  notify_ioctx = &ioctx;
+  notify_cookies.clear();
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.write(notify_oid, bl1, sizeof(buf), 0));
+
+  uint64_t handle;
+  WatchNotifyTestCtx2 ctx(this);
+  librados::AioCompletion *comp = cluster.aio_create_completion();
+  ASSERT_EQ(0, ioctx.aio_watch(notify_oid, comp, &handle, &ctx));
+  ASSERT_EQ(0, comp->wait_for_complete());
+  ASSERT_EQ(0, comp->get_return_value());
+  comp->release();
+
+  ASSERT_GT(ioctx.watch_check(handle), 0);
+  std::list<obj_watch_t> watches;
+  ASSERT_EQ(0, ioctx.list_watchers(notify_oid, &watches));
+  ASSERT_EQ(watches.size(), 1u);
+  bufferlist bl2, bl_reply;
+  ASSERT_EQ(0, ioctx.notify2(notify_oid, bl2, 300000, &bl_reply));
+  auto p = bl_reply.cbegin();
+  std::map<std::pair<uint64_t,uint64_t>,bufferlist> reply_map;
+  std::set<std::pair<uint64_t,uint64_t> > missed_map;
+  decode(reply_map, p);
+  decode(missed_map, p);
+  ASSERT_EQ(1u, notify_cookies.size());
+  ASSERT_EQ(1u, notify_cookies.count(handle));
+  ASSERT_EQ(1u, reply_map.size());
+  ASSERT_EQ(5u, reply_map.begin()->second.length());
+  ASSERT_EQ(0, strncmp("reply", reply_map.begin()->second.c_str(), 5));
+  ASSERT_EQ(0u, missed_map.size());
+  ASSERT_GT(ioctx.watch_check(handle), 0);
+
+  comp = cluster.aio_create_completion();
+  ioctx.aio_unwatch(handle, comp);
+  ASSERT_EQ(0, comp->wait_for_complete());
+  comp->release();
+}
+
+
+TEST_P(LibRadosWatchNotifyPP, AioNotify) {
+  notify_oid = "foo";
+  notify_ioctx = &ioctx;
+  notify_cookies.clear();
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.write(notify_oid, bl1, sizeof(buf), 0));
+  uint64_t handle;
+  WatchNotifyTestCtx2 ctx(this);
+  ASSERT_EQ(0, ioctx.watch2(notify_oid, &handle, &ctx));
+  ASSERT_GT(ioctx.watch_check(handle), 0);
+  std::list<obj_watch_t> watches;
+  ASSERT_EQ(0, ioctx.list_watchers(notify_oid, &watches));
+  ASSERT_EQ(watches.size(), 1u);
+  bufferlist bl2, bl_reply;
+  librados::AioCompletion *comp = cluster.aio_create_completion();
+  ASSERT_EQ(0, ioctx.aio_notify(notify_oid, comp, bl2, 300000, &bl_reply));
+  ASSERT_EQ(0, comp->wait_for_complete());
+  ASSERT_EQ(0, comp->get_return_value());
+  comp->release();
+  auto p = bl_reply.cbegin();
+  std::map<std::pair<uint64_t,uint64_t>,bufferlist> reply_map;
+  std::set<std::pair<uint64_t,uint64_t> > missed_map;
+  decode(reply_map, p);
+  decode(missed_map, p);
+  ASSERT_EQ(1u, notify_cookies.size());
+  ASSERT_EQ(1u, notify_cookies.count(handle));
+  ASSERT_EQ(1u, reply_map.size());
+  ASSERT_EQ(5u, reply_map.begin()->second.length());
+  ASSERT_EQ(0, strncmp("reply", reply_map.begin()->second.c_str(), 5));
+  ASSERT_EQ(0u, missed_map.size());
+  ASSERT_GT(ioctx.watch_check(handle), 0);
+  ioctx.unwatch2(handle);
+  cluster.watch_flush();
+}
+
+// --
+TEST_P(LibRadosWatchNotifyPP, WatchNotify2Timeout) {
+  notify_oid = "foo";
+  notify_ioctx = &ioctx;
+  notify_sleep = 3;  // 3s
+  notify_cookies.clear();
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.write(notify_oid, bl1, sizeof(buf), 0));
+  uint64_t handle;
+  WatchNotifyTestCtx2 ctx(this);
+  ASSERT_EQ(0, ioctx.watch2(notify_oid, &handle, &ctx));
+  ASSERT_GT(ioctx.watch_check(handle), 0);
+  std::list<obj_watch_t> watches;
+  ASSERT_EQ(0, ioctx.list_watchers(notify_oid, &watches));
+  ASSERT_EQ(watches.size(), 1u);
+  ASSERT_EQ(0u, notify_cookies.size());
+  bufferlist bl2, bl_reply;
+  std::cout << " trying..." << std::endl;
+  ASSERT_EQ(-ETIMEDOUT, ioctx.notify2(notify_oid, bl2, 1000 /* 1s */,
+                                     &bl_reply));
+  std::cout << " timed out" << std::endl;
+  ASSERT_GT(ioctx.watch_check(handle), 0);
+  ioctx.unwatch2(handle);
+
+  std::cout << " flushing" << std::endl;
+  librados::AioCompletion *comp = cluster.aio_create_completion();
+  cluster.aio_watch_flush(comp);
+  ASSERT_EQ(0, comp->wait_for_complete());
+  ASSERT_EQ(0, comp->get_return_value());
+  std::cout << " flushed" << std::endl;
+  comp->release();
+}
+
+TEST_P(LibRadosWatchNotifyPP, WatchNotify3) {
+  notify_oid = "foo";
+  notify_ioctx = &ioctx;
+  notify_cookies.clear();
+  uint32_t timeout = 12; // configured timeout
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.write(notify_oid, bl1, sizeof(buf), 0));
+  uint64_t handle;
+  WatchNotifyTestCtx2 ctx(this);
+  ASSERT_EQ(0, ioctx.watch3(notify_oid, &handle, &ctx, timeout));
+  ASSERT_GT(ioctx.watch_check(handle), 0);
+  std::list<obj_watch_t> watches;
+  ASSERT_EQ(0, ioctx.list_watchers(notify_oid, &watches));
+  ASSERT_EQ(watches.size(), 1u);
+  std::cout << "List watches" << std::endl;
+  for (std::list<obj_watch_t>::iterator it = watches.begin();
+    it != watches.end(); ++it) {
+    ASSERT_EQ(it->timeout_seconds, timeout);
+  }
+  bufferlist bl2, bl_reply;
+  std::cout << "notify2" << std::endl;
+  ASSERT_EQ(0, ioctx.notify2(notify_oid, bl2, 300000, &bl_reply));
+  std::cout << "notify2 done" << std::endl;
+  auto p = bl_reply.cbegin();
+  std::map<std::pair<uint64_t,uint64_t>,bufferlist> reply_map;
+  std::set<std::pair<uint64_t,uint64_t> > missed_map;
+  decode(reply_map, p);
+  decode(missed_map, p);
+  ASSERT_EQ(1u, notify_cookies.size());
+  ASSERT_EQ(1u, notify_cookies.count(handle));
+  ASSERT_EQ(1u, reply_map.size());
+  ASSERT_EQ(5u, reply_map.begin()->second.length());
+  ASSERT_EQ(0, strncmp("reply", reply_map.begin()->second.c_str(), 5));
+  ASSERT_EQ(0u, missed_map.size());
+  std::cout << "watch_check" << std::endl;
+  ASSERT_GT(ioctx.watch_check(handle), 0);
+  std::cout << "unwatch2" << std::endl;
+  ioctx.unwatch2(handle);
+
+  std::cout << " flushing" << std::endl;
+  cluster.watch_flush();
+  std::cout << "done" << std::endl;
+}
+// --
+
+INSTANTIATE_TEST_CASE_P(LibRadosWatchNotifyPPTests, LibRadosWatchNotifyPP,
+                       ::testing::Values("", "cache"));
index 9403810329ad923ff8f8786abd90e948a56fc0b1..14de7ca83b1f9462154ee2f6a728a40f1dd8026a 100644 (file)
@@ -4,26 +4,33 @@
 add_library(rados_striper_test STATIC TestCase.cc)
 target_link_libraries(rados_striper_test
   radostest
+  radostest-cxx
   GTest::GTest)
 
 add_executable(ceph_test_rados_striper_api_striping
   striping.cc
   )
-target_link_libraries(ceph_test_rados_striper_api_striping librados radosstriper
-  ${UNITTEST_LIBS} rados_striper_test)
+target_link_libraries(ceph_test_rados_striper_api_striping
+  ${UNITTEST_LIBS} rados_striper_test
+  radosstriper
+  librados
+  librados-cxx)
 install(TARGETS ceph_test_rados_striper_api_striping
   DESTINATION ${CMAKE_INSTALL_BINDIR})
 
 add_executable(ceph_test_rados_striper_api_io
   io.cc)
-target_link_libraries(ceph_test_rados_striper_api_io librados radosstriper
-  ${UNITTEST_LIBS} rados_striper_test)
+target_link_libraries(ceph_test_rados_striper_api_io
+  ${UNITTEST_LIBS} rados_striper_test
+  radosstriper
+  librados
+  librados-cxx)
 install(TARGETS ceph_test_rados_striper_api_io
   DESTINATION ${CMAKE_INSTALL_BINDIR})
 
 add_executable(ceph_test_rados_striper_api_aio
   aio.cc)
-target_link_libraries(ceph_test_rados_striper_api_aio librados radosstriper
+target_link_libraries(ceph_test_rados_striper_api_aio librados-cxx radosstriper
   ${UNITTEST_LIBS} rados_striper_test)
 install(TARGETS ceph_test_rados_striper_api_aio
   DESTINATION ${CMAKE_INSTALL_BINDIR})
index 5e1d34a9f0a4e77ea51d68ec9cc449bcb339e025..98e81f49c518d80a6c4a1060695c936ce2dc0032 100644 (file)
@@ -3,6 +3,7 @@
 
 #include <errno.h>
 #include "test/librados/test.h"
+#include "test/librados/test_cxx.h"
 #include "test/libradosstriper/TestCase.h"
 
 using namespace libradosstriper;
index d0f1a02f5b63f6eb13fce9c3d6aaa7c3b5e3d01e..002b0d2c0a9bb3ee761f3844207bbdb9b2beea14 100644 (file)
@@ -23,6 +23,9 @@ set(librbd_test
 add_library(rbd_test STATIC ${librbd_test})
 target_link_libraries(rbd_test PRIVATE
   rbd_test_support
+  radostest
+  radostest-cxx
+  librados
   Boost::thread
   GMock::GMock
   GTest::GTest)
@@ -120,8 +123,7 @@ target_link_libraries(unittest_librbd
   osdc
   ceph-common
   global
-  ${UNITTEST_LIBS}
-  radostest)
+  ${UNITTEST_LIBS})
 
 add_executable(ceph_test_librbd
   test_main.cc
@@ -135,6 +137,7 @@ target_link_libraries(ceph_test_librbd
   cls_journal_client
   cls_rbd_client
   librados
+  librados-cxx
   ${UNITTEST_LIBS}
   radostest)
 target_compile_definitions(ceph_test_librbd PRIVATE "TEST_LIBRBD_INTERNALS")
@@ -143,12 +146,14 @@ add_executable(ceph_test_librbd_api
   test_support.cc
   test_librbd.cc
   test_main.cc
-  $<TARGET_OBJECTS:libradostest_obj>
   $<TARGET_OBJECTS:common_texttable_obj>)
 target_link_libraries(ceph_test_librbd_api
   ceph-common
+  radostest
+  radostest-cxx
   librbd
   librados
+  librados-cxx
   ${UNITTEST_LIBS})
 
 add_executable(ceph_test_librbd_fsx
@@ -158,6 +163,7 @@ add_executable(ceph_test_librbd_fsx
 target_link_libraries(ceph_test_librbd_fsx
   librbd
   librados
+  librados-cxx
   journal
   global
   m
index 27b47eb66c942be9bc9d7e1455604de3d9f026d6..de5741a3bb12044031b33777ca0d0a2ded2faad9 100644 (file)
@@ -13,6 +13,7 @@
 #include "cls/rbd/cls_rbd_types.h"
 #include "librbd/internal.h"
 #include "test/librados/test.h"
+#include "test/librados/test_cxx.h"
 #include <iostream>
 #include <sstream>
 #include <stdlib.h>
index 432d626ef2a8f46979361867028b302c59858120..f77c5fdf682b20be3709c98b3aaf40d1169f5fd8 100644 (file)
@@ -41,6 +41,7 @@
 #include <vector>
 
 #include "test/librados/test.h"
+#include "test/librados/test_cxx.h"
 #include "test/librbd/test_support.h"
 #include "common/event_socket.h"
 #include "include/interval_set.h"
index 939f68b254a2620d86bd21c4b3548cc457b3781b..17b974b8e6612a0dd4c863b2208cf752a2a90efb 100644 (file)
@@ -4,6 +4,7 @@
 #include "include/rados/librados.hpp"
 #include "global/global_context.h"
 #include "test/librados/test.h"
+#include "test/librados/test_cxx.h"
 #include "gtest/gtest.h"
 #include <iostream>
 #include <string>
index a8c690f391ed073a2183397661b53b9f62239934..0871f3380d2832aad1bcc19a9461b5b42bba165a 100644 (file)
@@ -1,7 +1,7 @@
 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- 
 #include "include/rados/librados.h"
 #include "include/rados/librados.hpp"
-#include "test/librados/test.h"
+#include "test/librados/test_cxx.h"
 
 #include <semaphore.h>
 #include <errno.h>
index 64639d5ec8eba202fa409bc8b99d479f51ad29f0..565a82064dcccfe20a5be0cb69c0455599246f12 100644 (file)
@@ -6,7 +6,7 @@ add_executable(ceph_test_rados
   RadosModel.cc
   )
 target_link_libraries(ceph_test_rados
-  librados
+  librados-cxx
   global
   ${BLKID_LIBRARIES}
   ${CMAKE_DL_LIBS}
index 0b8497180ba506062905f9bbb3e03857c50acfd7..f6aa221bf809fb5b33eaec003b84a0ff075aa537 100644 (file)
@@ -64,7 +64,7 @@ target_link_libraries(unittest_rbd_mirror
   librados
   osdc
   global
-  radostest
+  radostest-cxx
   )
 
 add_executable(ceph_test_rbd_mirror
@@ -81,15 +81,15 @@ target_link_libraries(ceph_test_rbd_mirror
   cls_rbd_client
   cls_journal_client
   rbd_types
-  librados
-  radostest
+  librados-cxx
+  radostest-cxx
   ${UNITTEST_LIBS}
   )
 
 add_executable(ceph_test_rbd_mirror_random_write
   random_write.cc)
 target_link_libraries(ceph_test_rbd_mirror_random_write
-  librbd librados global)
+  librbd librados-cxx global)
 
 install(TARGETS
   ceph_test_rbd_mirror
index a69085cc12e1b5260050b1bfef164d0ad5bc6b04..05ca2c1080fca1116074963ff6d34343506f623a 100644 (file)
@@ -10,7 +10,7 @@
 #include "tools/rbd_mirror/ServiceDaemon.h"
 #include "tools/rbd_mirror/Types.h"
 #include "test/rbd_mirror/test_fixture.h"
-#include "test/librados/test.h"
+#include "test/librados/test_cxx.h"
 #include "test/librbd/test_support.h"
 #include "gtest/gtest.h"
 #include <boost/scope_exit.hpp>
index 51eb8b560c66b3bd8a5650b4b6e3907a6c431c4b..a4e2f869287a1a690ca660dc166bde4c74a7f23e 100644 (file)
@@ -40,7 +40,7 @@
 #include "tools/rbd_mirror/Threads.h"
 #include "tools/rbd_mirror/Types.h"
 
-#include "test/librados/test.h"
+#include "test/librados/test_cxx.h"
 #include "gtest/gtest.h"
 
 using rbd::mirror::RadosRef;
index 1924928f0c291945210df9c1786a3d62da354aaf..d495a9ea5c4e9df79051b643730f1792f47933cb 100644 (file)
@@ -11,7 +11,7 @@
 #include "tools/rbd_mirror/InstanceWatcher.h"
 #include "tools/rbd_mirror/Threads.h"
 
-#include "test/librados/test.h"
+#include "test/librados/test_cxx.h"
 #include "gtest/gtest.h"
 
 using rbd::mirror::InstanceWatcher;
index 63118ebfe4f59bf44daf363ffe5f43de0c006921..86dc6d904b7b85122d5402e1375aba55d78556fa 100644 (file)
@@ -10,7 +10,7 @@
 #include "tools/rbd_mirror/LeaderWatcher.h"
 #include "tools/rbd_mirror/Threads.h"
 
-#include "test/librados/test.h"
+#include "test/librados/test_cxx.h"
 #include "gtest/gtest.h"
 
 using librbd::util::unique_lock_name;
index 1f31f177337e65f36668551d6975f55c44b9964e..46098807c4b0a3ef69e07be6065878a25162dcb5 100644 (file)
@@ -21,7 +21,7 @@
 #include "tools/rbd_mirror/Threads.h"
 #include "tools/rbd_mirror/Types.h"
 #include "tools/rbd_mirror/pool_watcher/Types.h"
-#include "test/librados/test.h"
+#include "test/librados/test_cxx.h"
 #include "gtest/gtest.h"
 #include <boost/scope_exit.hpp>
 #include <iostream>
index 1c312a9a871d0ffbe0c1d5a6f9c8d6d2b1052bfe..0e30a17a1252934d05c962e720ab30dfefbd2260 100644 (file)
@@ -8,7 +8,7 @@
 #include "librbd/ImageCtx.h"
 #include "librbd/ImageState.h"
 #include "librbd/Operations.h"
-#include "test/librados/test.h"
+#include "test/librados/test_cxx.h"
 #include "tools/rbd_mirror/Threads.h"
 
 namespace rbd {
index eb31af1e506b3bba604e3a1eb6aac0c08da50371..d5859cac919c0b4ee10b465268124b1c7c5e72fa 100644 (file)
@@ -3,7 +3,7 @@
 
 #include "include/rados/librados.hpp"
 #include "global/global_context.h"
-#include "test/librados/test.h"
+#include "test/librados/test_cxx.h"
 #include "gtest/gtest.h"
 #include <iostream>
 #include <string>
index ec2b7057df7d776b5e68d65427c2c18a71e6bc82..c6942b7738bca7ecf00743fc044325a9750d2fd3 100644 (file)
@@ -3,7 +3,7 @@
 
 #include "librados/AioCompletionImpl.h"
 #include "librbd/ManagedLock.h"
-#include "test/librados/test.h"
+#include "test/librados/test_cxx.h"
 #include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
 #include "test/librados_test_stub/MockTestMemRadosClient.h"
 #include "test/librbd/mock/MockImageCtx.h"
index 8604328b59c2bd49b56c58f0323de6f6a9e9ed1e..003fc2b0ac5b8db06bf1b7107e60af7c09131702 100644 (file)
@@ -3,7 +3,7 @@
 #include "include/utime.h"
 #include "common/Thread.h"
 #include "common/Clock.h"
-#include "test/librados/test.h"
+#include "test/librados/test_cxx.h"
 
 #include "gtest/gtest.h"
 #include <semaphore.h>
@@ -14,7 +14,7 @@
 #include <string>
 #include <atomic>
 
-#include "test/librados/TestCase.h"
+#include "test/librados/testcase_cxx.h"
 
 
 using namespace librados;
index 6ac9035553c2af4e24a29247c25a23f63409caed..6579d5f6d91269e46a7b23e710099d66799c2f3b 100644 (file)
@@ -8,7 +8,7 @@ set(rados_srcs
   ${PROJECT_SOURCE_DIR}/src/osd/ECUtil.cc)
 add_executable(rados ${rados_srcs})
 
-target_link_libraries(rados librados global ${BLKID_LIBRARIES} ${CMAKE_DL_LIBS})
+target_link_libraries(rados librados-cxx global ${BLKID_LIBRARIES} ${CMAKE_DL_LIBS})
 if(WITH_LIBRADOSSTRIPER)
   target_link_libraries(rados radosstriper)
 else()
@@ -22,11 +22,11 @@ target_link_libraries(ceph_scratchtool librados global)
 install(TARGETS ceph_scratchtool DESTINATION bin)
 
 add_executable(ceph_scratchtoolpp scratchtoolpp.cc)
-target_link_libraries(ceph_scratchtoolpp librados global)
+target_link_libraries(ceph_scratchtoolpp librados-cxx global)
 install(TARGETS ceph_scratchtoolpp DESTINATION bin)
 
 add_executable(ceph_radosacl radosacl.cc)
-target_link_libraries(ceph_radosacl librados global)
+target_link_libraries(ceph_radosacl librados-cxx global)
 install(TARGETS ceph_radosacl DESTINATION bin)
 
 install(PROGRAMS
index 2cca8dc034c854def7ddbd1742b481cdad8646a2..dbfc74e6a33b9f0a5cd60c8ddd507dd20f6e494f 100644 (file)
@@ -9,7 +9,7 @@ set(cephfs_journal_tool_srcs
   RoleSelector.cc
   MDSUtility.cc)
 add_executable(cephfs-journal-tool ${cephfs_journal_tool_srcs})
-target_link_libraries(cephfs-journal-tool librados mds osdc global
+target_link_libraries(cephfs-journal-tool librados-cxx mds osdc global
   ${BLKID_LIBRARIES} ${CMAKE_DL_LIBS})
 
 set(cephfs_table_tool_srcs
@@ -18,7 +18,7 @@ set(cephfs_table_tool_srcs
   RoleSelector.cc
   MDSUtility.cc)
 add_executable(cephfs-table-tool ${cephfs_table_tool_srcs})
-target_link_libraries(cephfs-table-tool librados mds osdc global
+target_link_libraries(cephfs-table-tool librados-cxx mds osdc global
   ${BLKID_LIBRARIES} ${CMAKE_DL_LIBS})
 
 set(cephfs_data_scan_srcs
@@ -28,7 +28,7 @@ set(cephfs_data_scan_srcs
   PgFiles.cc
   MDSUtility.cc)
 add_executable(cephfs-data-scan ${cephfs_data_scan_srcs})
-target_link_libraries(cephfs-data-scan librados cephfs mds osdc global
+target_link_libraries(cephfs-data-scan librados-cxx cephfs mds osdc global
   cls_cephfs_client
   ${BLKID_LIBRARIES} ${CMAKE_DL_LIBS})
 
index 76cc57904fd5dc1398dc8ff2742993b75bb21659..e28a21a42da2726c1b20feb56a3476ab04dbb78f 100644 (file)
@@ -45,7 +45,7 @@ set(rbd_srcs
 add_executable(rbd ${rbd_srcs}
   $<TARGET_OBJECTS:common_texttable_obj>)
 set_target_properties(rbd PROPERTIES OUTPUT_NAME rbd)
-target_link_libraries(rbd librbd librados
+target_link_libraries(rbd librbd librados-cxx
   cls_journal_client cls_rbd_client
   rbd_types
   journal
index fb39f9c52215c3a67f7083fd98631f970bafd2d3..1ad8ad34af38ed82a6f62b564f39021d2268a167 100644 (file)
@@ -59,7 +59,7 @@ target_link_libraries(rbd-mirror
   rbd_internal
   rbd_types
   journal
-  librados
+  librados-cxx
   osdc
   cls_rbd_client
   cls_lock_client
index d7ce811e9634f7b19952428a87595c90fd1c02ff..73ea99137864563f1f7f35c5bd776bd3c5fcd5b8 100644 (file)
@@ -1,3 +1,3 @@
 add_executable(rbd-nbd rbd-nbd.cc)
-target_link_libraries(rbd-nbd librbd librados global)
+target_link_libraries(rbd-nbd librbd librados-cxx global)
 install(TARGETS rbd-nbd DESTINATION bin)