From: Kefu Chai Date: Tue, 9 Jan 2018 09:19:28 +0000 (+0800) Subject: librados: move C++ APIs into libradospp X-Git-Tag: v14.1.0~1018^2~12 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=3730d10623650ce8569be96b28cbba599a9a0db6;p=ceph.git librados: move C++ APIs into libradospp 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 --- diff --git a/qa/workunits/rados/test.sh b/qa/workunits/rados/test.sh index 082c638b0ec..a2f5e0499db 100755 --- a/qa/workunits/rados/test.sh +++ b/qa/workunits/rados/test.sh @@ -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 \ diff --git a/src/cls/lua/cls_lua_client.h b/src/cls/lua/cls_lua_client.h index e37906993c9..c1ed4369f63 100644 --- a/src/cls/lua/cls_lua_client.h +++ b/src/cls/lua/cls_lua_client.h @@ -2,7 +2,7 @@ #define CLS_LUA_CLIENT_HPP #include -#include "include/buffer_fwd.h" // for bufferlist +#include "include/rados/librados.hpp" namespace librados { class IoCtx; diff --git a/src/librados/AioCompletionImpl.h b/src/librados/AioCompletionImpl.h index e7b895358f6..98fa4e0c883 100644 --- a/src/librados/AioCompletionImpl.h +++ b/src/librados/AioCompletionImpl.h @@ -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" diff --git a/src/librados/CMakeLists.txt b/src/librados/CMakeLists.txt index 34d6af6a919..8943e44be23 100644 --- a/src/librados/CMakeLists.txt +++ b/src/librados/CMakeLists.txt @@ -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 + $) + +add_library(rados_c_api OBJECT + librados_c.cc) +add_library(rados_cxx_api OBJECT + librados_cxx.cc) +add_library(rados_cxx STATIC + $) 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} + $) if(ENABLE_SHARED) - add_library(librados ${CEPH_SHARED} - $ - $ - $) 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 - $ - $) -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} + $) +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() diff --git a/src/librados/RadosClient.cc b/src/librados/RadosClient.cc index ff057257b03..4adc3755cf0 100644 --- a/src/librados/RadosClient.cc +++ b/src/librados/RadosClient.cc @@ -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* pgs) + std::vector* pgs) { vector cmd = { "{\"prefix\": \"pg ls\"," diff --git a/src/librados/librados.cc b/src/librados/librados.cc deleted file mode 100644 index 67eebe76436..00000000000 --- a/src/librados/librados.cc +++ /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 - * - * 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 - -#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 "librados/AioCompletionImpl.h" -#include "librados/IoCtxImpl.h" -#include "librados/PoolAsyncCompletionImpl.h" -#include "librados/RadosClient.h" -#include "librados/RadosXattrIter.h" -#include "librados/ListObjectImpl.h" -#include - -#include -#include -#include -#include -#include -#include - -#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 *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 *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 *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 *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 *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 *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 *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 &keys, - std::map *map, - int *prval) -{ - ::ObjectOperation *o = &impl->o; - o->omap_get_vals_by_keys(keys, map, prval); -} - -void librados::ObjectOperation::omap_cmp( - const std::map > &assertions, - int *prval) -{ - ::ObjectOperation *o = &impl->o; - o->omap_cmp(assertions, prval); -} - -void librados::ObjectReadOperation::list_watchers( - list *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 *out_vals) -{ - bool first = true; - string start_after = orig_start_after; - bool more = true; - while (max_return > 0 && more) { - std::map 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 *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 *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 &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 &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& 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& 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& 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 *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 *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 *out_keys) -{ - bool first = true; - string start_after = orig_start_after; - bool more = true; - while (max_return > 0 && more) { - std::set 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 *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& keys, - std::map *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& 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& 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& snaps) -{ - object_t obj(oid); - vector 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& snaps, - const blkin_trace_info *trace_info) -{ - object_t obj(oid); - vector 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& snaps, int flags, - const blkin_trace_info *trace_info) -{ - object_t obj(oid); - vector 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& snaps) -{ - vector snv; - snv.resize(snaps.size()); - for (unsigned i=0; iset_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 *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 *lockers) -{ - std::list tmp_lockers; - map 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::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 > *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 *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 *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(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& 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 *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& metadata) ///< static metadata about daemon -{ - return client->service_daemon_register(service, name, metadata); -} - -int librados::Rados::service_daemon_update_status( - std::map&& 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& v) -{ - std::list > pools; - int r = client->pool_list(pools); - if (r < 0) { - return r; - } - - v.clear(); - for (std::list >::iterator it = pools.begin(); - it != pools.end(); ++it) { - v.push_back(it->second); - } - return 0; -} - -int librados::Rados::pool_list2(std::list >& 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 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 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 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 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& v, - stats_map& result) -{ - map rawresult; - int r = client->get_pool_stats(v, rawresult); - for (map::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& v, - std::map& result) -{ - stats_map m; - int r = get_pool_stats(v, m); - if (r < 0) - return r; - for (map::iterator p = m.begin(); - p != m.end(); - ++p) { - result[p->first][string()] = p->second; - } - return r; -} - -int librados::Rados::get_pool_stats(std::list& v, - string& category, // unused - std::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* pgs) -{ - std::vector 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* 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* 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(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(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(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(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 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 args; - for (int i=0; icct->_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 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 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 > 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 >::const_iterator i = pools.begin(); - std::list >::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 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* 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 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 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 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 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 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 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 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 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 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 snv; - snv.resize(num_snaps); - for (int i=0; iset_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 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; isnap_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(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(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 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::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(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(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(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(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(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 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::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 > 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 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 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 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 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 values; - std::map::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 keys; - explicit C_OmapKeysIter(RadosOmapIter *iter) : iter(iter) {} - void finish(int r) override { - // map each key to an empty bl - for (std::set::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& 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 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 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(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(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(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 *result, - ObjectCursor *next) -{ - ceph_assert(result != nullptr); - ceph_assert(next != nullptr); - result->clear(); - - C_SaferCond cond; - hobject_t next_hash; - std::list 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::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 *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 *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 index 00000000000..d6f0c225b29 --- /dev/null +++ b/src/librados/librados_c.cc @@ -0,0 +1,3737 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include + +#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 "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 + +#include +#include +#include +#include +#include +#include + +#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(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(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(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(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 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 args; + for (int i=0; icct->_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 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 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 > 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 >::const_iterator i = pools.begin(); + std::list >::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 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* 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 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 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 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 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 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 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 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 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 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 snv; + snv.resize(num_snaps); + for (int i=0; iset_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 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; isnap_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(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(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 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::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(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(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(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(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(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 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::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 > 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 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 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 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 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 values; + std::map::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 keys; + explicit C_OmapKeysIter(RadosOmapIter *iter) : iter(iter) {} + void finish(int r) override { + // map each key to an empty bl + for (std::set::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& 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 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 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(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(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(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 index 00000000000..c1d2f3fd10c --- /dev/null +++ b/src/librados/librados_cxx.cc @@ -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 + * + * 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 + +#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 "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 +#include +#include +#include +#include +#include + +#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 *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 *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 *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 *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 *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 *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 *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 &keys, + std::map *map, + int *prval) +{ + ::ObjectOperation *o = &impl->o; + o->omap_get_vals_by_keys(keys, map, prval); +} + +void librados::ObjectOperation::omap_cmp( + const std::map > &assertions, + int *prval) +{ + ::ObjectOperation *o = &impl->o; + o->omap_cmp(assertions, prval); +} + +void librados::ObjectReadOperation::list_watchers( + list *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 *out_vals) +{ + bool first = true; + string start_after = orig_start_after; + bool more = true; + while (max_return > 0 && more) { + std::map 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 *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 *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 &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 &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& 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& 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& 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 *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 *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 *out_keys) +{ + bool first = true; + string start_after = orig_start_after; + bool more = true; + while (max_return > 0 && more) { + std::set 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 *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& keys, + std::map *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& 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& 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& snaps) +{ + object_t obj(oid); + vector 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& snaps, + const blkin_trace_info *trace_info) +{ + object_t obj(oid); + vector 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& snaps, int flags, + const blkin_trace_info *trace_info) +{ + object_t obj(oid); + vector 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& snaps) +{ + vector snv; + snv.resize(snaps.size()); + for (unsigned i=0; iset_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 *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 *lockers) +{ + std::list tmp_lockers; + map 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::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 > *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 *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 *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(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& 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 *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& metadata) ///< static metadata about daemon +{ + return client->service_daemon_register(service, name, metadata); +} + +int librados::Rados::service_daemon_update_status( + std::map&& 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& v) +{ + std::list > pools; + int r = client->pool_list(pools); + if (r < 0) { + return r; + } + + v.clear(); + for (std::list >::iterator it = pools.begin(); + it != pools.end(); ++it) { + v.push_back(it->second); + } + return 0; +} + +int librados::Rados::pool_list2(std::list >& 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 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 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 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 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& v, + stats_map& result) +{ + map rawresult; + int r = client->get_pool_stats(v, rawresult); + for (map::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& v, + std::map& result) +{ + stats_map m; + int r = get_pool_stats(v, m); + if (r < 0) + return r; + for (map::iterator p = m.begin(); + p != m.end(); + ++p) { + result[p->first][string()] = p->second; + } + return r; +} + +int librados::Rados::get_pool_stats(std::list& v, + string& category, // unused + std::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* pgs) +{ + std::vector 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* 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* 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 *result, + ObjectCursor *next) +{ + ceph_assert(result != nullptr); + ceph_assert(next != nullptr); + result->clear(); + + C_SaferCond cond; + hobject_t next_hash; + std::list 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::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 *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 *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 index 00000000000..b696de871b6 --- /dev/null +++ b/src/librados/librados_tp.cc @@ -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 index 00000000000..109bf9ab680 --- /dev/null +++ b/src/librados/librados_util.cc @@ -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 index 00000000000..ab9c461f42a --- /dev/null +++ b/src/librados/librados_util.h @@ -0,0 +1,34 @@ +#include +#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; + } +}; diff --git a/src/libradosstriper/CMakeLists.txt b/src/libradosstriper/CMakeLists.txt index 5b2dd7566af..d9d022f675f 100644 --- a/src/libradosstriper/CMakeLists.txt +++ b/src/libradosstriper/CMakeLists.txt @@ -3,10 +3,12 @@ set(libradosstriper_srcs RadosStriperImpl.cc MultiAioCompletionImpl.cc) add_library(radosstriper ${CEPH_SHARED} - ${libradosstriper_srcs} - $) + ${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 diff --git a/src/librbd/CMakeLists.txt b/src/librbd/CMakeLists.txt index 2ba963ed0db..5501dc633c2 100644 --- a/src/librbd/CMakeLists.txt +++ b/src/librbd/CMakeLists.txt @@ -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 diff --git a/src/rbd_replay/CMakeLists.txt b/src/rbd_replay/CMakeLists.txt index e9d3804c5f5..5ffae423be4 100644 --- a/src/rbd_replay/CMakeLists.txt +++ b/src/rbd_replay/CMakeLists.txt @@ -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) diff --git a/src/rgw/CMakeLists.txt b/src/rgw/CMakeLists.txt index 4d488a0eba3..939282733c2 100644 --- a/src/rgw/CMakeLists.txt +++ b/src/rgw/CMakeLists.txt @@ -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 diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 43243785864..f9eb45b5bd8 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -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} diff --git a/src/test/cls_hello/CMakeLists.txt b/src/test/cls_hello/CMakeLists.txt index 4ed22132f26..1363d746b6a 100644 --- a/src/test/cls_hello/CMakeLists.txt +++ b/src/test/cls_hello/CMakeLists.txt @@ -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 diff --git a/src/test/cls_hello/test_cls_hello.cc b/src/test/cls_hello/test_cls_hello.cc index 55386a86fb2..7cf30125f0b 100644 --- a/src/test/cls_hello/test_cls_hello.cc +++ b/src/test/cls_hello/test_cls_hello.cc @@ -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; diff --git a/src/test/cls_journal/CMakeLists.txt b/src/test/cls_journal/CMakeLists.txt index 3f2cad9c01f..08d47655a79 100644 --- a/src/test/cls_journal/CMakeLists.txt +++ b/src/test/cls_journal/CMakeLists.txt @@ -4,13 +4,13 @@ add_executable(ceph_test_cls_journal $) 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}) diff --git a/src/test/cls_journal/test_cls_journal.cc b/src/test/cls_journal/test_cls_journal.cc index 9df173d6202..2082bbc2935 100644 --- a/src/test/cls_journal/test_cls_journal.cc +++ b/src/test/cls_journal/test_cls_journal.cc @@ -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 #include diff --git a/src/test/cls_lock/CMakeLists.txt b/src/test/cls_lock/CMakeLists.txt index 49214e17e68..53f36725c56 100644 --- a/src/test/cls_lock/CMakeLists.txt +++ b/src/test/cls_lock/CMakeLists.txt @@ -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 diff --git a/src/test/cls_lock/test_cls_lock.cc b/src/test/cls_lock/test_cls_lock.cc index 37d10a19cbc..c9bc2dd4b89 100644 --- a/src/test/cls_lock/test_cls_lock.cc +++ b/src/test/cls_lock/test_cls_lock.cc @@ -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; diff --git a/src/test/cls_log/CMakeLists.txt b/src/test/cls_log/CMakeLists.txt index acbd31d6bd9..c27d9843a1c 100644 --- a/src/test/cls_log/CMakeLists.txt +++ b/src/test/cls_log/CMakeLists.txt @@ -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} diff --git a/src/test/cls_log/test_cls_log.cc b/src/test/cls_log/test_cls_log.cc index daf1ceeb9e9..05485035ae0 100644 --- a/src/test/cls_log/test_cls_log.cc +++ b/src/test/cls_log/test_cls_log.cc @@ -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 #include diff --git a/src/test/cls_lua/CMakeLists.txt b/src/test/cls_lua/CMakeLists.txt index 1bc8c4cce6e..cfb6ba1171b 100644 --- a/src/test/cls_lua/CMakeLists.txt +++ b/src/test/cls_lua/CMakeLists.txt @@ -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}) diff --git a/src/test/cls_lua/test_cls_lua.cc b/src/test/cls_lua/test_cls_lua.cc index 1cd3a00d2fa..b6727826659 100644 --- a/src/test/cls_lua/test_cls_lua.cc +++ b/src/test/cls_lua/test_cls_lua.cc @@ -1,9 +1,9 @@ #include #include #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" diff --git a/src/test/cls_numops/CMakeLists.txt b/src/test/cls_numops/CMakeLists.txt index b52d7f9894a..00108991d39 100644 --- a/src/test/cls_numops/CMakeLists.txt +++ b/src/test/cls_numops/CMakeLists.txt @@ -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 diff --git a/src/test/cls_numops/test_cls_numops.cc b/src/test/cls_numops/test_cls_numops.cc index 844caf993cf..ebb2256208b 100644 --- a/src/test/cls_numops/test_cls_numops.cc +++ b/src/test/cls_numops/test_cls_numops.cc @@ -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; diff --git a/src/test/cls_rbd/CMakeLists.txt b/src/test/cls_rbd/CMakeLists.txt index 77596dd690b..d42deb08848 100644 --- a/src/test/cls_rbd/CMakeLists.txt +++ b/src/test/cls_rbd/CMakeLists.txt @@ -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}) diff --git a/src/test/cls_rbd/test_cls_rbd.cc b/src/test/cls_rbd/test_cls_rbd.cc index 99a0a55b981..3490dad1607 100644 --- a/src/test/cls_rbd/test_cls_rbd.cc +++ b/src/test/cls_rbd/test_cls_rbd.cc @@ -17,7 +17,7 @@ #include "librbd/Types.h" #include "gtest/gtest.h" -#include "test/librados/test.h" +#include "test/librados/test_cxx.h" #include #include diff --git a/src/test/cls_refcount/CMakeLists.txt b/src/test/cls_refcount/CMakeLists.txt index 9983828456f..e9aaeaa6a25 100644 --- a/src/test/cls_refcount/CMakeLists.txt +++ b/src/test/cls_refcount/CMakeLists.txt @@ -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 diff --git a/src/test/cls_refcount/test_cls_refcount.cc b/src/test/cls_refcount/test_cls_refcount.cc index 73ba1db13b3..b481ca0f240 100644 --- a/src/test/cls_refcount/test_cls_refcount.cc +++ b/src/test/cls_refcount/test_cls_refcount.cc @@ -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 #include diff --git a/src/test/cls_rgw/CMakeLists.txt b/src/test/cls_rgw/CMakeLists.txt index ca53e9dfc7d..79409db53b7 100644 --- a/src/test/cls_rgw/CMakeLists.txt +++ b/src/test/cls_rgw/CMakeLists.txt @@ -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}) diff --git a/src/test/cls_rgw/test_cls_rgw.cc b/src/test/cls_rgw/test_cls_rgw.cc index d09e867ec75..7e78acf8a1c 100644 --- a/src/test/cls_rgw/test_cls_rgw.cc +++ b/src/test/cls_rgw/test_cls_rgw.cc @@ -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 diff --git a/src/test/cls_sdk/CMakeLists.txt b/src/test/cls_sdk/CMakeLists.txt index d75971276c2..cd05900d56f 100644 --- a/src/test/cls_sdk/CMakeLists.txt +++ b/src/test/cls_sdk/CMakeLists.txt @@ -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 diff --git a/src/test/cls_sdk/test_cls_sdk.cc b/src/test/cls_sdk/test_cls_sdk.cc index af3452bb5c6..44b32196b3a 100644 --- a/src/test/cls_sdk/test_cls_sdk.cc +++ b/src/test/cls_sdk/test_cls_sdk.cc @@ -1,7 +1,7 @@ #include #include -#include "test/librados/test.h" +#include "test/librados/test_cxx.h" #include "gtest/gtest.h" using namespace librados; diff --git a/src/test/cls_version/CMakeLists.txt b/src/test/cls_version/CMakeLists.txt index 7334ce43624..d8e43878d69 100644 --- a/src/test/cls_version/CMakeLists.txt +++ b/src/test/cls_version/CMakeLists.txt @@ -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 ) diff --git a/src/test/cls_version/test_cls_version.cc b/src/test/cls_version/test_cls_version.cc index 4c2d59500df..9ecea1f2cd2 100644 --- a/src/test/cls_version/test_cls_version.cc +++ b/src/test/cls_version/test_cls_version.cc @@ -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 #include diff --git a/src/test/journal/CMakeLists.txt b/src/test/journal/CMakeLists.txt index 213630e941c..99e0f8ae6e6 100644 --- a/src/test/journal/CMakeLists.txt +++ b/src/test/journal/CMakeLists.txt @@ -27,6 +27,6 @@ target_link_libraries(unittest_journal cls_journal_client rados_test_stub librados - radostest + radostest-cxx global ) diff --git a/src/test/journal/RadosTestFixture.cc b/src/test/journal/RadosTestFixture.cc index 1c4ed84075e..61a9967f75f 100644 --- a/src/test/journal/RadosTestFixture.cc +++ b/src/test/journal/RadosTestFixture.cc @@ -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" diff --git a/src/test/librados/CMakeLists.txt b/src/test/librados/CMakeLists.txt index 33ac156f9d1..1e7ccff92f1 100644 --- a/src/test/librados/CMakeLists.txt +++ b/src/test/librados/CMakeLists.txt @@ -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 $) -set(libradostest_srcs - test_common.cc - TestCase.cc) + add_library(radostest STATIC - ${libradostest_srcs} - $) -target_link_libraries(radostest + test_common.cc + TestCase.cc + test.cc + $) +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_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_link_libraries(ceph_test_rados_api_misc librados global ${UNITTEST_LIBS} radostest) +add_executable(ceph_test_rados_api_misc_pp + misc_cxx.cc + $) +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_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 diff --git a/src/test/librados/TestCase.cc b/src/test/librados/TestCase.cc index 37dbd2a923b..1066587b5ec 100644 --- a/src/test/librados/TestCase.cc +++ b/src/test/librados/TestCase.cc @@ -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(); -} - diff --git a/src/test/librados/TestCase.h b/src/test/librados/TestCase.h index 94f668eac01..15fcfeb73bc 100644 --- a/src/test/librados/TestCase.h +++ b/src/test/librados/TestCase.h @@ -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 @@ -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 { -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 { -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 diff --git a/src/test/librados/aio.cc b/src/test/librados/aio.cc index 2d14e0f9170..503f992c069 100644 --- a/src/test/librados/aio.cc +++ b/src/test/librados/aio.cc @@ -1,22 +1,23 @@ -#include "common/errno.h" +#include +#include +#include +#include +#include +#include + #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 -#include -#include -#include -#include -#include + +#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 &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 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 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(-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 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 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 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 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 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 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 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 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 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 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 my_completion - (test_data.m_cluster.aio_create_completion - (nullptr, nullptr, nullptr)); - std::map 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::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 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 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 - 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 - 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 to_set; - { - boost::scoped_ptr 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 my_completion(cluster.aio_create_completion(0, 0, 0)); - ObjectReadOperation op; - map > assertions; - bufferlist val; - val.append(string("bar")); - assertions["foo"] = pair(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 my_completion(cluster.aio_create_completion(0, 0, 0)); - ObjectReadOperation op; - - set set_got; - map map_got; - - set to_get; - map got3; - - map 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 my_completion(cluster.aio_create_completion(0, 0, 0)); - ObjectWriteOperation op; - set 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 my_completion(cluster.aio_create_completion(0, 0, 0)); - ObjectReadOperation op; - - set 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 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 my_completion(cluster.aio_create_completion(0, 0, 0)); - ObjectReadOperation op; - - set 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 my_completion(cluster.aio_create_completion(0, 0, 0)); - ObjectWriteOperation op; - bufferlist bl; - bl.append("some data"); - map 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 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 my_completion(cluster.aio_create_completion(0, 0, 0)); - ObjectReadOperation op; - set 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 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 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 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 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 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 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 to_set; - { - boost::scoped_ptr 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 - 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 - 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 - 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 - 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 - 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 index 00000000000..b812930766b --- /dev/null +++ b/src/test/librados/aio_cxx.cc @@ -0,0 +1,2443 @@ +#include +#include +#include +#include +#include +#include + +#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 &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 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 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(-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 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 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 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 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 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 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 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 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 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 my_completion + (test_data.m_cluster.aio_create_completion + (nullptr, nullptr, nullptr)); + std::map 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::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 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 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 + 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 + 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 to_set; + { + boost::scoped_ptr 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 my_completion(cluster.aio_create_completion(0, 0, 0)); + ObjectReadOperation op; + map > assertions; + bufferlist val; + val.append(string("bar")); + assertions["foo"] = pair(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 my_completion(cluster.aio_create_completion(0, 0, 0)); + ObjectReadOperation op; + + set set_got; + map map_got; + + set to_get; + map got3; + + map 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 my_completion(cluster.aio_create_completion(0, 0, 0)); + ObjectWriteOperation op; + set 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 my_completion(cluster.aio_create_completion(0, 0, 0)); + ObjectReadOperation op; + + set 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 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 my_completion(cluster.aio_create_completion(0, 0, 0)); + ObjectReadOperation op; + + set 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 my_completion(cluster.aio_create_completion(0, 0, 0)); + ObjectWriteOperation op; + bufferlist bl; + bl.append("some data"); + map 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 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 my_completion(cluster.aio_create_completion(0, 0, 0)); + ObjectReadOperation op; + set 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 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 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 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 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 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 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 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 to_set; + { + boost::scoped_ptr 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 + 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 + 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 + 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 + 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 + 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/c_write_operations.cc b/src/test/librados/c_write_operations.cc index 325a2f0a3e0..558d0942f93 100644 --- a/src/test/librados/c_write_operations.cc +++ b/src/test/librados/c_write_operations.cc @@ -1,10 +1,10 @@ // Tests for the C API coverage of atomic write operations #include +#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(); diff --git a/src/test/librados/cls.cc b/src/test/librados/cls.cc index 10744b2b9b8..c4f24954dbe 100644 --- a/src/test/librados/cls.cc +++ b/src/test/librados/cls.cc @@ -1,13 +1,13 @@ -#include "include/rados/librados.h" -#include "include/rados/librados.hpp" -#include "test/librados/test.h" - -#include "gtest/gtest.h" #include #include #include #include +#include "gtest/gtest.h" + +#include "include/rados/librados.hpp" +#include "test/librados/test_cxx.h" + using namespace librados; using std::map; using std::ostringstream; diff --git a/src/test/librados/cmd.cc b/src/test/librados/cmd.cc index 043d98328f5..49465c3bcce 100644 --- a/src/test/librados/cmd.cc +++ b/src/test/librados/cmd.cc @@ -13,7 +13,6 @@ #include #include -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 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 index 00000000000..d67e2613b31 --- /dev/null +++ b/src/test/librados/cmd_cxx.cc @@ -0,0 +1,92 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include +#include +#include +#include +#include + +#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)); +} + diff --git a/src/test/librados/io.cc b/src/test/librados/io.cc index 9e56c79a603..fce3de62122 100644 --- a/src/test/librados/io.cc +++ b/src/test/librados/io.cc @@ -4,7 +4,6 @@ #include #include "include/rados/librados.h" -#include "include/rados/librados.hpp" #include "include/encoding.h" #include "include/err.h" #include "include/scope_guard.h" @@ -14,13 +13,10 @@ #include #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 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(-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 attrset; - ASSERT_EQ(0, ioctx.getxattrs("foo", attrset)); - for (std::map::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 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 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 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 attrset; - ASSERT_EQ(0, ioctx.getxattrs("foo", attrset)); - for (std::map::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 index 00000000000..5930849100b --- /dev/null +++ b/src/test/librados/io_cxx.cc @@ -0,0 +1,904 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -* +// vim: ts=8 sw=2 smarttab + +#include +#include + +#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 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(-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 attrset; + ASSERT_EQ(0, ioctx.getxattrs("foo", attrset)); + for (std::map::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 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 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 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 attrset; + ASSERT_EQ(0, ioctx.getxattrs("foo", attrset)); + for (std::map::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/list.cc b/src/test/librados/list.cc index 045cd37f35b..a1bda0aaff4 100644 --- a/src/test/librados/list.cc +++ b/src/test/librados/list.cc @@ -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& myset, @@ -233,108 +133,6 @@ TEST_F(LibRadosList, ListObjectsNS) { rados_nobjects_list_close(ctx); } -static void check_listpp(std::set& myset, IoCtx& ioctx, const std::string &check_nspace) -{ - NObjectIterator iter(ioctx.nobjects_begin()); - std::set 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 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 saw_obj; - std::set 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 > 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 >::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 oid_to_ns; - - for (int i=0; i cursor_to_obj; - - int count = 0; - - librados::ObjectCursor seek_cursor; - - map > 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 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& 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 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 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 saw_obj; - std::set 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 > 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 >::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 saw_obj; - ObjectCursor c = ioctx.object_list_begin(); - ObjectCursor end = ioctx.object_list_end(); - while(!ioctx.object_list_is_end(c)) - { - std::vector 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 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 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 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 index 00000000000..45368b39cf3 --- /dev/null +++ b/src/test/librados/list_cxx.cc @@ -0,0 +1,773 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +#include +#include +#include + +#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& myset, IoCtx& ioctx, const std::string &check_nspace) +{ + NObjectIterator iter(ioctx.nobjects_begin()); + std::set 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 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 saw_obj; + std::set 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 > 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 >::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 oid_to_ns; + + for (int i=0; i cursor_to_obj; + + int count = 0; + + librados::ObjectCursor seek_cursor; + + map > 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 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& 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 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 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 saw_obj; + std::set 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 > 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 >::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 saw_obj; + ObjectCursor c = ioctx.object_list_begin(); + ObjectCursor end = ioctx.object_list_end(); + while(!ioctx.object_list_is_end(c)) + { + std::vector 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 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 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 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/lock.cc b/src/test/librados/lock.cc index ef1033f5e66..53ed300e7ba 100644 --- a/src/test/librados/lock.cc +++ b/src/test/librados/lock.cc @@ -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" @@ -12,55 +11,21 @@ #include using namespace std::chrono_literals; -using namespace librados; typedef RadosTest LibRadosLock; -typedef RadosTestPP LibRadosLockPP; typedef RadosTestEC LibRadosLockEC; -typedef RadosTestECPP LibRadosLockECPP; -template> -Return wait_until(const std::chrono::duration& rel_time, - const std::chrono::duration& 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)...); - if (ret == expected) { - return ret; - } - std::this_thread::sleep_for(s); - } - return func(std::forward(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 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 lockers; - ASSERT_EQ(1, ioctx.list_lockers("foo", "TestLockPP7", &exclusive, &tag, &lockers)); - std::list::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 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::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 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 lockers; - ASSERT_EQ(1, ioctx.list_lockers("foo", "TestLockECPP7", &exclusive, &tag, &lockers)); - std::list::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 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::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 index 00000000000..3007b22686a --- /dev/null +++ b/src/test/librados/lock_cxx.cc @@ -0,0 +1,193 @@ +#include +#include +#include +#include +#include +#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 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 lockers; + ASSERT_EQ(1, ioctx.list_lockers("foo", "TestLockPP7", &exclusive, &tag, &lockers)); + std::list::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 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::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 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 lockers; + ASSERT_EQ(1, ioctx.list_lockers("foo", "TestLockECPP7", &exclusive, &tag, &lockers)); + std::list::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 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::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/misc.cc b/src/test/librados/misc.cc index 50f87186566..5f7fdd889f5 100644 --- a/src/test/librados/misc.cc +++ b/src/test/librados/misc.cc @@ -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 m; - decode(header, p); - decode(m, p); - map::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 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 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::iterator p = omap.begin(); p != omap.end(); ++p) { - map::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(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 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 -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(rand() % (126 - 33) + 33); - } - content_bl.append(content); - ASSERT_EQ(0, ioctx.write("foo", content_bl, content_bl.length(), 0)); - } -}; - -template -class LibRadosChecksumParams { -public: - typedef AlgT alg_t; - typedef ValueT value_t; - static const rados_checksum_type_t type = _type; -}; - -typedef ::testing::Types< - LibRadosChecksumParams, - LibRadosChecksumParams, - LibRadosChecksumParams - > 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 checksum_bls(csum_count); - std::vector 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(&expected_value)); - Checksummer::template calculate( - 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 expected_values(csum_count); - bufferptr expected_values_bp = buffer::create_static( - csum_count * sizeof(typename TestFixture::value_t), - reinterpret_cast(&expected_values[0])); - - Checksummer::template calculate( - 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 expected_apps = {"rados"}; - std::set 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 expected_meta; - std::map 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 index 00000000000..cf11c19d04c --- /dev/null +++ b/src/test/librados/misc_cxx.cc @@ -0,0 +1,1151 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +#include +#include +#include +#include +#include + +#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 m; + decode(header, p); + decode(m, p); + map::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 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 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::iterator p = omap.begin(); p != omap.end(); ++p) { + map::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(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 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 +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(rand() % (126 - 33) + 33); + } + content_bl.append(content); + ASSERT_EQ(0, ioctx.write("foo", content_bl, content_bl.length(), 0)); + } +}; + +template +class LibRadosChecksumParams { +public: + typedef AlgT alg_t; + typedef ValueT value_t; + static const rados_checksum_type_t type = _type; +}; + +typedef ::testing::Types< + LibRadosChecksumParams, + LibRadosChecksumParams, + LibRadosChecksumParams + > 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 checksum_bls(csum_count); + std::vector 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(&expected_value)); + Checksummer::template calculate( + 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 expected_values(csum_count); + bufferptr expected_values_bp = buffer::create_static( + csum_count * sizeof(typename TestFixture::value_t), + reinterpret_cast(&expected_values[0])); + + Checksummer::template calculate( + 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 expected_apps = {"rados"}; + std::set 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 expected_meta; + std::map 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); +} diff --git a/src/test/librados/pool.cc b/src/test/librados/pool.cc index 74c89595bf1..14906651922 100644 --- a/src/test/librados/pool.cc +++ b/src/test/librados/pool.cc @@ -1,9 +1,8 @@ -#include "include/rados/librados.h" -#include "test/librados/test.h" - -#include "gtest/gtest.h" #include #include +#include "gtest/gtest.h" +#include "include/rados/librados.h" +#include "test/librados/test.h" #define POOL_LIST_BUF_SZ 32768 diff --git a/src/test/librados/service.cc b/src/test/librados/service.cc index da37fb3d62f..9bc9cd50691 100644 --- a/src/test/librados/service.cc +++ b/src/test/librados/service.cc @@ -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("mgr_tick_period")); - std::this_thread::sleep_for(g_conf().get_val( - "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 index 00000000000..40869f0f8d9 --- /dev/null +++ b/src/test/librados/service_cxx.cc @@ -0,0 +1,104 @@ +#include +#include +#include +#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("mgr_tick_period")); + std::this_thread::sleep_for(g_conf().get_val( + "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/snapshots.cc b/src/test/librados/snapshots.cc index 49bd9961efc..966ec19add6 100644 --- a/src/test/librados/snapshots.cc +++ b/src/test/librados/snapshots.cc @@ -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 -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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 index 00000000000..be3087f9140 --- /dev/null +++ b/src/test/librados/snapshots_cxx.cc @@ -0,0 +1,689 @@ +#include +#include +#include + +#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 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 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 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 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 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 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 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 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 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 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 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 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 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/stat.cc b/src/test/librados/stat.cc index 9641bb8138d..fd667742738 100644 --- a/src/test/librados/stat.cc +++ b/src/test/librados/stat.cc @@ -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 #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 v; - std::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 v; - std::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 index 00000000000..d0d0116dfec --- /dev/null +++ b/src/test/librados/stat_cxx.cc @@ -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 v; + std::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 v; + std::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)); +} diff --git a/src/test/librados/test.cc b/src/test/librados/test.cc index 04fd0452e66..fdfbc54199c 100644 --- a/src/test/librados/test.cc +++ b/src/test/librados/test.cc @@ -18,22 +18,6 @@ #include #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 &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 &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(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& 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.h b/src/test/librados/test.h index 8b505957fd0..b3e0115fb46 100644 --- a/src/test/librados/test.h +++ b/src/test/librados/test.h @@ -16,45 +16,17 @@ #define CEPH_TEST_RADOS_API_TEST_H #include "include/rados/librados.h" -#include "include/rados/librados.hpp" +#include "test/librados/test_shared.h" #include #include #include -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 &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 &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& 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 index 00000000000..9349aece8dc --- /dev/null +++ b/src/test/librados/test_cxx.cc @@ -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 +#include +#include +#include +#include +#include +#include +#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 &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 &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(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 index 00000000000..1d11d69236d --- /dev/null +++ b/src/test/librados/test_cxx.h @@ -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 &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 &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 index 00000000000..8b50d112e45 --- /dev/null +++ b/src/test/librados/test_shared.cc @@ -0,0 +1,44 @@ +#include "test_shared.h" + +#include +#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& 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 index 00000000000..29e23b471ca --- /dev/null +++ b/src/test/librados/test_shared.h @@ -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 +#include +#include +#include +#include + +#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& extents, + ceph::bufferlist& actual); +class TestAlarm +{ +public: + TestAlarm() { + alarm(1200); + } + ~TestAlarm() { + alarm(0); + } +}; + +template> +Return wait_until(const std::chrono::duration& rel_time, + const std::chrono::duration& 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)...); + if (ret == expected) { + return ret; + } + std::this_thread::sleep_for(s); + } + return func(std::forward(args)...); +} diff --git a/src/test/librados/testcase_cxx.cc b/src/test/librados/testcase_cxx.cc new file mode 100644 index 00000000000..0a71fa39ca2 --- /dev/null +++ b/src/test/librados/testcase_cxx.cc @@ -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 +#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 index 00000000000..637ec11eefc --- /dev/null +++ b/src/test/librados/testcase_cxx.h @@ -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 { +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 { +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 index d7f30e6e51d..00000000000 --- a/src/test/librados/tier.cc +++ /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 -#include -#include -#include - -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 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 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 my_snaps; - for (int snap=0; snap<4; ++snap) { - // create a snapshot, clone - vector 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 - 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 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 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 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 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::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 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 > 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 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 hitsets; - for (int i=0; i > 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 > 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 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 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 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 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 keys; - keys.insert("somekey"); - std::map 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 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 > 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 hitsets; - for (int i=0; i > 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 > 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(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(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 index 00000000000..e6acd304771 --- /dev/null +++ b/src/test/librados/tier_cxx.cc @@ -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 +#include +#include +#include + +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 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 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 my_snaps; + for (int snap=0; snap<4; ++snap) { + // create a snapshot, clone + vector 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 - 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 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 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 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 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::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 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 > 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 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 hitsets; + for (int i=0; i > 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 > 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 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 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 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 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 keys; + keys.insert("somekey"); + std::map 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 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 > 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 hitsets; + for (int i=0; i > 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 > 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(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(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/watch_notify.cc b/src/test/librados/watch_notify.cc index bae3c0852fe..54e54b0803a 100644 --- a/src/test/librados/watch_notify.cc +++ b/src/test/librados/watch_notify.cc @@ -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" @@ -12,10 +11,7 @@ #include #include -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 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 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 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 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,bufferlist> reply_map; - std::set > 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 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,bufferlist> reply_map; - std::set > 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 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,bufferlist> reply_map; - std::set > 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 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 watches; - ASSERT_EQ(0, ioctx.list_watchers(notify_oid, &watches)); - ASSERT_EQ(watches.size(), 1u); - std::cout << "List watches" << std::endl; - for (std::list::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,bufferlist> reply_map; - std::set > 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 index 00000000000..0289cba8b70 --- /dev/null +++ b/src/test/librados/watch_notify_cxx.cc @@ -0,0 +1,364 @@ +#include +#include +#include +#include +#include + +#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 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 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 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 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,bufferlist> reply_map; + std::set > 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 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,bufferlist> reply_map; + std::set > 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 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,bufferlist> reply_map; + std::set > 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 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 watches; + ASSERT_EQ(0, ioctx.list_watchers(notify_oid, &watches)); + ASSERT_EQ(watches.size(), 1u); + std::cout << "List watches" << std::endl; + for (std::list::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,bufferlist> reply_map; + std::set > 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")); diff --git a/src/test/libradosstriper/CMakeLists.txt b/src/test/libradosstriper/CMakeLists.txt index 9403810329a..14de7ca83b1 100644 --- a/src/test/libradosstriper/CMakeLists.txt +++ b/src/test/libradosstriper/CMakeLists.txt @@ -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}) diff --git a/src/test/libradosstriper/TestCase.cc b/src/test/libradosstriper/TestCase.cc index 5e1d34a9f0a..98e81f49c51 100644 --- a/src/test/libradosstriper/TestCase.cc +++ b/src/test/libradosstriper/TestCase.cc @@ -3,6 +3,7 @@ #include #include "test/librados/test.h" +#include "test/librados/test_cxx.h" #include "test/libradosstriper/TestCase.h" using namespace libradosstriper; diff --git a/src/test/librbd/CMakeLists.txt b/src/test/librbd/CMakeLists.txt index d0f1a02f5b6..002b0d2c0a9 100644 --- a/src/test/librbd/CMakeLists.txt +++ b/src/test/librbd/CMakeLists.txt @@ -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_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 diff --git a/src/test/librbd/test_fixture.cc b/src/test/librbd/test_fixture.cc index 27b47eb66c9..de5741a3bb1 100644 --- a/src/test/librbd/test_fixture.cc +++ b/src/test/librbd/test_fixture.cc @@ -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 #include #include diff --git a/src/test/librbd/test_librbd.cc b/src/test/librbd/test_librbd.cc index 432d626ef2a..f77c5fdf682 100644 --- a/src/test/librbd/test_librbd.cc +++ b/src/test/librbd/test_librbd.cc @@ -41,6 +41,7 @@ #include #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" diff --git a/src/test/librbd/test_main.cc b/src/test/librbd/test_main.cc index 939f68b254a..17b974b8e66 100644 --- a/src/test/librbd/test_main.cc +++ b/src/test/librbd/test_main.cc @@ -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 #include diff --git a/src/test/multi_stress_watch.cc b/src/test/multi_stress_watch.cc index a8c690f391e..0871f3380d2 100644 --- a/src/test/multi_stress_watch.cc +++ b/src/test/multi_stress_watch.cc @@ -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 #include diff --git a/src/test/osd/CMakeLists.txt b/src/test/osd/CMakeLists.txt index 64639d5ec8e..565a82064dc 100644 --- a/src/test/osd/CMakeLists.txt +++ b/src/test/osd/CMakeLists.txt @@ -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} diff --git a/src/test/rbd_mirror/CMakeLists.txt b/src/test/rbd_mirror/CMakeLists.txt index 0b8497180ba..f6aa221bf80 100644 --- a/src/test/rbd_mirror/CMakeLists.txt +++ b/src/test/rbd_mirror/CMakeLists.txt @@ -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 diff --git a/src/test/rbd_mirror/test_ClusterWatcher.cc b/src/test/rbd_mirror/test_ClusterWatcher.cc index a69085cc12e..05ca2c1080f 100644 --- a/src/test/rbd_mirror/test_ClusterWatcher.cc +++ b/src/test/rbd_mirror/test_ClusterWatcher.cc @@ -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 diff --git a/src/test/rbd_mirror/test_ImageReplayer.cc b/src/test/rbd_mirror/test_ImageReplayer.cc index 51eb8b560c6..a4e2f869287 100644 --- a/src/test/rbd_mirror/test_ImageReplayer.cc +++ b/src/test/rbd_mirror/test_ImageReplayer.cc @@ -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; diff --git a/src/test/rbd_mirror/test_InstanceWatcher.cc b/src/test/rbd_mirror/test_InstanceWatcher.cc index 1924928f0c2..d495a9ea5c4 100644 --- a/src/test/rbd_mirror/test_InstanceWatcher.cc +++ b/src/test/rbd_mirror/test_InstanceWatcher.cc @@ -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; diff --git a/src/test/rbd_mirror/test_LeaderWatcher.cc b/src/test/rbd_mirror/test_LeaderWatcher.cc index 63118ebfe4f..86dc6d904b7 100644 --- a/src/test/rbd_mirror/test_LeaderWatcher.cc +++ b/src/test/rbd_mirror/test_LeaderWatcher.cc @@ -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; diff --git a/src/test/rbd_mirror/test_PoolWatcher.cc b/src/test/rbd_mirror/test_PoolWatcher.cc index 1f31f177337..46098807c4b 100644 --- a/src/test/rbd_mirror/test_PoolWatcher.cc +++ b/src/test/rbd_mirror/test_PoolWatcher.cc @@ -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 #include diff --git a/src/test/rbd_mirror/test_fixture.cc b/src/test/rbd_mirror/test_fixture.cc index 1c312a9a871..0e30a17a125 100644 --- a/src/test/rbd_mirror/test_fixture.cc +++ b/src/test/rbd_mirror/test_fixture.cc @@ -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 { diff --git a/src/test/rbd_mirror/test_main.cc b/src/test/rbd_mirror/test_main.cc index eb31af1e506..d5859cac919 100644 --- a/src/test/rbd_mirror/test_main.cc +++ b/src/test/rbd_mirror/test_main.cc @@ -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 #include diff --git a/src/test/rbd_mirror/test_mock_InstanceWatcher.cc b/src/test/rbd_mirror/test_mock_InstanceWatcher.cc index ec2b7057df7..c6942b7738b 100644 --- a/src/test/rbd_mirror/test_mock_InstanceWatcher.cc +++ b/src/test/rbd_mirror/test_mock_InstanceWatcher.cc @@ -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" diff --git a/src/test/test_stress_watch.cc b/src/test/test_stress_watch.cc index 8604328b59c..003fc2b0ac5 100644 --- a/src/test/test_stress_watch.cc +++ b/src/test/test_stress_watch.cc @@ -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 @@ -14,7 +14,7 @@ #include #include -#include "test/librados/TestCase.h" +#include "test/librados/testcase_cxx.h" using namespace librados; diff --git a/src/tools/CMakeLists.txt b/src/tools/CMakeLists.txt index 6ac9035553c..6579d5f6d91 100644 --- a/src/tools/CMakeLists.txt +++ b/src/tools/CMakeLists.txt @@ -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 diff --git a/src/tools/cephfs/CMakeLists.txt b/src/tools/cephfs/CMakeLists.txt index 2cca8dc034c..dbfc74e6a33 100644 --- a/src/tools/cephfs/CMakeLists.txt +++ b/src/tools/cephfs/CMakeLists.txt @@ -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}) diff --git a/src/tools/rbd/CMakeLists.txt b/src/tools/rbd/CMakeLists.txt index 76cc57904fd..e28a21a42da 100644 --- a/src/tools/rbd/CMakeLists.txt +++ b/src/tools/rbd/CMakeLists.txt @@ -45,7 +45,7 @@ set(rbd_srcs add_executable(rbd ${rbd_srcs} $) 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 diff --git a/src/tools/rbd_mirror/CMakeLists.txt b/src/tools/rbd_mirror/CMakeLists.txt index fb39f9c5221..1ad8ad34af3 100644 --- a/src/tools/rbd_mirror/CMakeLists.txt +++ b/src/tools/rbd_mirror/CMakeLists.txt @@ -59,7 +59,7 @@ target_link_libraries(rbd-mirror rbd_internal rbd_types journal - librados + librados-cxx osdc cls_rbd_client cls_lock_client diff --git a/src/tools/rbd_nbd/CMakeLists.txt b/src/tools/rbd_nbd/CMakeLists.txt index d7ce811e963..73ea9913786 100644 --- a/src/tools/rbd_nbd/CMakeLists.txt +++ b/src/tools/rbd_nbd/CMakeLists.txt @@ -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)