summaryrefslogtreecommitdiffstats
path: root/src/include/neorados
diff options
context:
space:
mode:
Diffstat (limited to 'src/include/neorados')
-rw-r--r--src/include/neorados/RADOS.hpp1152
-rw-r--r--src/include/neorados/RADOS_Decodable.hpp107
l---------src/include/neorados/buffer_fwd.h1
l---------src/include/neorados/completion.h1
4 files changed, 1261 insertions, 0 deletions
diff --git a/src/include/neorados/RADOS.hpp b/src/include/neorados/RADOS.hpp
new file mode 100644
index 000000000..244442bcf
--- /dev/null
+++ b/src/include/neorados/RADOS.hpp
@@ -0,0 +1,1152 @@
+// -*- 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) 2018 Red Hat <contact@redhat.com>
+ * Author: Adam C. Emerson
+ *
+ * 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.
+ *
+ */
+
+#ifndef NEORADOS_RADOS_HPP
+#define NEORADOS_RADOS_HPP
+
+#include <cstddef>
+#include <memory>
+#include <tuple>
+#include <string>
+#include <string_view>
+#include <type_traits>
+#include <variant>
+
+#include <boost/asio.hpp>
+
+#include <boost/container/flat_map.hpp>
+#include <boost/container/flat_set.hpp>
+#include <boost/uuid/uuid.hpp>
+
+#include <boost/system/error_code.hpp>
+
+// Will be in C++20!
+
+#include "include/expected.hpp"
+
+// Had better be in C++20. Why is this not in Boost?
+
+#include "include/function2.hpp"
+
+// Things broken out so we can decode them in Objecter.
+
+#include "include/neorados/RADOS_Decodable.hpp"
+
+// Needed for type erasure and template support. We can't really avoid
+// it.
+
+#include "common/async/completion.h"
+
+// These are needed for RGW, but in general as a 'shiny new interface'
+// we should try to use forward declarations and provide standard alternatives.
+
+#include "include/common_fwd.h"
+
+#include "include/buffer.h"
+#include "include/rados/librados_fwd.hpp"
+
+#include "common/ceph_time.h"
+
+namespace neorados {
+using namespace std::literals;
+
+class Object;
+class IOContext;
+}
+namespace std {
+template<>
+struct hash<neorados::Object>;
+template<>
+struct hash<neorados::IOContext>;
+}
+
+namespace neorados {
+namespace detail {
+class Client;
+}
+
+class RADOS;
+
+// Exists mostly so that repeated operations on the same object don't
+// have to pay for the string copy to construct an object_t.
+
+class Object final {
+ friend RADOS;
+ friend std::hash<Object>;
+
+public:
+ Object();
+ Object(const char* s);
+ Object(std::string_view s);
+ Object(std::string&& s);
+ Object(const std::string& s);
+ ~Object();
+
+ Object(const Object& o);
+ Object& operator =(const Object& o);
+
+ Object(Object&& o);
+ Object& operator =(Object&& o);
+
+ operator std::string_view() const;
+
+ friend std::ostream& operator <<(std::ostream& m, const Object& o);
+ friend bool operator <(const Object& lhs, const Object& rhs);
+ friend bool operator <=(const Object& lhs, const Object& rhs);
+ friend bool operator >=(const Object& lhs, const Object& rhs);
+ friend bool operator >(const Object& lhs, const Object& rhs);
+
+ friend bool operator ==(const Object& lhs, const Object& rhs);
+ friend bool operator !=(const Object& lhs, const Object& rhs);
+
+private:
+
+ static constexpr std::size_t impl_size = 4 * 8;
+ std::aligned_storage_t<impl_size> impl;
+};
+
+// Not the same as the librados::IoCtx, but it does gather together
+// some of the same metadata. Since we're likely to do multiple
+// operations in the same pool or namespace, it doesn't make sense to
+// redo a bunch of lookups and string copies.
+
+class IOContext final {
+ friend RADOS;
+ friend std::hash<IOContext>;
+
+public:
+
+ IOContext();
+ explicit IOContext(std::int64_t pool);
+ IOContext(std::int64_t _pool, std::string_view _ns);
+ IOContext(std::int64_t _pool, std::string&& _ns);
+ ~IOContext();
+
+ IOContext(const IOContext& rhs);
+ IOContext& operator =(const IOContext& rhs);
+
+ IOContext(IOContext&& rhs);
+ IOContext& operator =(IOContext&& rhs);
+
+ std::int64_t pool() const;
+ void pool(std::int64_t _pool);
+
+ std::string_view ns() const;
+ void ns(std::string_view _ns);
+ void ns(std::string&& _ns);
+
+ std::optional<std::string_view> key() const;
+ void key(std::string_view _key);
+ void key(std::string&& _key);
+ void clear_key();
+
+ std::optional<std::int64_t> hash() const;
+ void hash(std::int64_t _hash);
+ void clear_hash();
+
+ std::optional<std::uint64_t> read_snap() const;
+ void read_snap(std::optional<std::uint64_t> _snapid);
+
+ // I can't actually move-construct here since snapid_t is its own
+ // separate class type, not an alias.
+ std::optional<
+ std::pair<std::uint64_t,
+ std::vector<std::uint64_t>>> write_snap_context() const;
+ void write_snap_context(std::optional<
+ std::pair<std::uint64_t,
+ std::vector<std::uint64_t>>> snapc);
+
+ bool full_try() const;
+ void full_try(bool _full_try);
+
+ friend std::ostream& operator <<(std::ostream& m, const IOContext& o);
+ friend bool operator <(const IOContext& lhs, const IOContext& rhs);
+ friend bool operator <=(const IOContext& lhs, const IOContext& rhs);
+ friend bool operator >=(const IOContext& lhs, const IOContext& rhs);
+ friend bool operator >(const IOContext& lhs, const IOContext& rhs);
+
+ friend bool operator ==(const IOContext& lhs, const IOContext& rhs);
+ friend bool operator !=(const IOContext& lhs, const IOContext& rhs);
+
+private:
+
+ static constexpr std::size_t impl_size = 16 * 8;
+ std::aligned_storage_t<impl_size> impl;
+};
+
+inline constexpr std::string_view all_nspaces("\001"sv);
+
+enum class cmpxattr_op : std::uint8_t {
+ eq = 1,
+ ne = 2,
+ gt = 3,
+ gte = 4,
+ lt = 5,
+ lte = 6
+};
+
+namespace alloc_hint {
+enum alloc_hint_t {
+ sequential_write = 1,
+ random_write = 2,
+ sequential_read = 4,
+ random_read = 8,
+ append_only = 16,
+ immutable = 32,
+ shortlived = 64,
+ longlived = 128,
+ compressible = 256,
+ incompressible = 512
+};
+}
+
+class Op {
+ friend RADOS;
+
+public:
+
+ Op(const Op&) = delete;
+ Op& operator =(const Op&) = delete;
+ Op(Op&&);
+ Op& operator =(Op&&);
+ ~Op();
+
+ void set_excl();
+ void set_failok();
+ void set_fadvise_random();
+ void set_fadvise_sequential();
+ void set_fadvise_willneed();
+ void set_fadvise_dontneed();
+ void set_fadvise_nocache();
+
+ void cmpext(uint64_t off, ceph::buffer::list&& cmp_bl, std::size_t* s);
+ void cmpxattr(std::string_view name, cmpxattr_op op,
+ const ceph::buffer::list& val);
+ void cmpxattr(std::string_view name, cmpxattr_op op, std::uint64_t val);
+ void assert_version(uint64_t ver);
+ void assert_exists();
+ void cmp_omap(const boost::container::flat_map<
+ std::string,
+ std::pair<ceph::buffer::list, int>>& assertions);
+
+ void exec(std::string_view cls, std::string_view method,
+ const ceph::buffer::list& inbl,
+ ceph::buffer::list* out,
+ boost::system::error_code* ec = nullptr);
+ void exec(std::string_view cls, std::string_view method,
+ const ceph::buffer::list& inbl,
+ fu2::unique_function<void(boost::system::error_code,
+ const ceph::buffer::list&) &&> f);
+ void exec(std::string_view cls, std::string_view method,
+ const ceph::buffer::list& inbl,
+ fu2::unique_function<void(boost::system::error_code, int,
+ const ceph::buffer::list&) &&> f);
+ void exec(std::string_view cls, std::string_view method,
+ const ceph::buffer::list& inbl,
+ boost::system::error_code* ec = nullptr);
+
+
+ // Flags that apply to all ops in the operation vector
+ void balance_reads();
+ void localize_reads();
+ void order_reads_writes();
+ void ignore_cache();
+ void skiprwlocks();
+ void ignore_overlay();
+ void full_try();
+ void full_force();
+ void ignore_redirect();
+ void ordersnap();
+ void returnvec();
+
+ std::size_t size() const;
+ using Signature = void(boost::system::error_code);
+ using Completion = ceph::async::Completion<Signature>;
+
+ friend std::ostream& operator <<(std::ostream& m, const Op& o);
+protected:
+ Op();
+ static constexpr std::size_t impl_size = 85 * 8;
+ std::aligned_storage_t<impl_size> impl;
+};
+
+// This class is /not/ thread-safe. If you want you can wrap it in
+// something that locks it.
+
+class ReadOp final : public Op {
+ friend RADOS;
+
+public:
+
+ ReadOp() = default;
+ ReadOp(const ReadOp&) = delete;
+ ReadOp(ReadOp&&) = default;
+
+ ReadOp& operator =(const ReadOp&) = delete;
+ ReadOp& operator =(ReadOp&&) = default;
+
+ void read(size_t off, uint64_t len, ceph::buffer::list* out,
+ boost::system::error_code* ec = nullptr);
+ void get_xattr(std::string_view name, ceph::buffer::list* out,
+ boost::system::error_code* ec = nullptr);
+ void get_omap_header(ceph::buffer::list*,
+ boost::system::error_code* ec = nullptr);
+
+ void sparse_read(uint64_t off, uint64_t len,
+ ceph::buffer::list* out,
+ std::vector<std::pair<std::uint64_t, std::uint64_t>>* extents,
+ boost::system::error_code* ec = nullptr);
+
+ void stat(std::uint64_t* size, ceph::real_time* mtime,
+ boost::system::error_code* ec = nullptr);
+
+ void get_omap_keys(std::optional<std::string_view> start_after,
+ std::uint64_t max_return,
+ boost::container::flat_set<std::string>* keys,
+ bool* truncated,
+ boost::system::error_code* ec = nullptr);
+
+
+ void get_xattrs(boost::container::flat_map<std::string,
+ ceph::buffer::list>* kv,
+ boost::system::error_code* ec = nullptr);
+
+ void get_omap_vals(std::optional<std::string_view> start_after,
+ std::optional<std::string_view> filter_prefix,
+ uint64_t max_return,
+ boost::container::flat_map<std::string,
+ ceph::buffer::list>* kv,
+ bool* truncated,
+ boost::system::error_code* ec = nullptr);
+
+
+ void get_omap_vals_by_keys(const boost::container::flat_set<std::string>& keys,
+ boost::container::flat_map<std::string,
+ ceph::buffer::list>* kv,
+ boost::system::error_code* ec = nullptr);
+
+ void list_watchers(std::vector<struct ObjWatcher>* watchers,
+ boost::system::error_code* ec = nullptr);
+
+ void list_snaps(struct SnapSet* snaps,
+ boost::system::error_code* ec = nullptr);
+};
+
+class WriteOp final : public Op {
+ friend RADOS;
+public:
+
+ WriteOp() = default;
+ WriteOp(const WriteOp&) = delete;
+ WriteOp(WriteOp&&) = default;
+
+ WriteOp& operator =(const WriteOp&) = delete;
+ WriteOp& operator =(WriteOp&&) = default;
+
+ void set_mtime(ceph::real_time t);
+ void create(bool exclusive);
+ void write(uint64_t off, ceph::buffer::list&& bl);
+ void write_full(ceph::buffer::list&& bl);
+ void writesame(std::uint64_t off, std::uint64_t write_len,
+ ceph::buffer::list&& bl);
+ void append(ceph::buffer::list&& bl);
+ void remove();
+ void truncate(uint64_t off);
+ void zero(uint64_t off, uint64_t len);
+ void rmxattr(std::string_view name);
+ void setxattr(std::string_view name,
+ ceph::buffer::list&& bl);
+ void rollback(uint64_t snapid);
+ void set_omap(const boost::container::flat_map<std::string,
+ ceph::buffer::list>& map);
+ void set_omap_header(ceph::buffer::list&& bl);
+ void clear_omap();
+ void rm_omap_keys(const boost::container::flat_set<std::string>& to_rm);
+ void set_alloc_hint(uint64_t expected_object_size,
+ uint64_t expected_write_size,
+ alloc_hint::alloc_hint_t flags);
+};
+
+
+struct FSStats {
+ uint64_t kb;
+ uint64_t kb_used;
+ uint64_t kb_avail;
+ uint64_t num_objects;
+};
+
+// From librados.h, maybe move into a common file. But I want to see
+// if we need/want to amend/add/remove anything first.
+struct PoolStats {
+ /// space used in bytes
+ uint64_t num_bytes;
+ /// space used in KB
+ uint64_t num_kb;
+ /// number of objects in the pool
+ uint64_t num_objects;
+ /// number of clones of objects
+ uint64_t num_object_clones;
+ /// num_objects * num_replicas
+ uint64_t num_object_copies;
+ /// number of objects missing on primary
+ uint64_t num_objects_missing_on_primary;
+ /// number of objects found on no OSDs
+ uint64_t num_objects_unfound;
+ /// number of objects replicated fewer times than they should be
+ /// (but found on at least one OSD)
+ uint64_t num_objects_degraded;
+ /// number of objects read
+ uint64_t num_rd;
+ /// objects read in KB
+ uint64_t num_rd_kb;
+ /// number of objects written
+ uint64_t num_wr;
+ /// objects written in KB
+ uint64_t num_wr_kb;
+ /// bytes originally provided by user
+ uint64_t num_user_bytes;
+ /// bytes passed compression
+ uint64_t compressed_bytes_orig;
+ /// bytes resulted after compression
+ uint64_t compressed_bytes;
+ /// bytes allocated at storage
+ uint64_t compressed_bytes_alloc;
+};
+
+// Placement group, for PG commands
+struct PG {
+ uint64_t pool;
+ uint32_t seed;
+};
+
+class Cursor final {
+public:
+ static Cursor begin();
+ static Cursor end();
+
+ Cursor();
+ Cursor(const Cursor&);
+ Cursor& operator =(const Cursor&);
+ Cursor(Cursor&&);
+ Cursor& operator =(Cursor&&);
+ ~Cursor();
+
+ friend bool operator ==(const Cursor& lhs,
+ const Cursor& rhs);
+ friend bool operator !=(const Cursor& lhs,
+ const Cursor& rhs);
+ friend bool operator <(const Cursor& lhs,
+ const Cursor& rhs);
+ friend bool operator <=(const Cursor& lhs,
+ const Cursor& rhs);
+ friend bool operator >=(const Cursor& lhs,
+ const Cursor& rhs);
+ friend bool operator >(const Cursor& lhs,
+ const Cursor& rhs);
+
+ std::string to_str() const;
+ static std::optional<Cursor> from_str(const std::string& s);
+
+private:
+ struct end_magic_t {};
+ Cursor(end_magic_t);
+ Cursor(void*);
+ friend RADOS;
+ static constexpr std::size_t impl_size = 16 * 8;
+ std::aligned_storage_t<impl_size> impl;
+};
+
+class RADOS final
+{
+public:
+ static constexpr std::tuple<uint32_t, uint32_t, uint32_t> version() {
+ return {0, 0, 1};
+ }
+
+ using BuildSig = void(boost::system::error_code, RADOS);
+ using BuildComp = ceph::async::Completion<BuildSig>;
+ class Builder {
+ std::optional<std::string> conf_files;
+ std::optional<std::string> cluster;
+ std::optional<std::string> name;
+ std::vector<std::pair<std::string, std::string>> configs;
+ bool no_default_conf = false;
+ bool no_mon_conf = false;
+
+ public:
+ Builder() = default;
+ Builder& add_conf_file(std::string_view v);
+ Builder& set_cluster(std::string_view c) {
+ cluster = std::string(c);
+ return *this;
+ }
+ Builder& set_name(std::string_view n) {
+ name = std::string(n);
+ return *this;
+ }
+ Builder& set_no_default_conf() {
+ no_default_conf = true;
+ return *this;
+ }
+ Builder& set_no_mon_conf() {
+ no_mon_conf = true;
+ return *this;
+ }
+ Builder& set_conf_option(std::string_view opt, std::string_view val) {
+ configs.emplace_back(std::string(opt), std::string(val));
+ return *this;
+ }
+
+ template<typename CompletionToken>
+ auto build(boost::asio::io_context& ioctx, CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, BuildSig> init(token);
+ build(ioctx,
+ BuildComp::create(ioctx.get_executor(),
+ std::move(init.completion_handler)));
+ return init.result.get();
+ }
+
+ private:
+ void build(boost::asio::io_context& ioctx,
+ std::unique_ptr<BuildComp> c);
+ };
+
+
+ template<typename CompletionToken>
+ static auto make_with_cct(CephContext* cct,
+ boost::asio::io_context& ioctx,
+ CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, BuildSig> init(token);
+ make_with_cct(cct, ioctx,
+ BuildComp::create(ioctx.get_executor(),
+ std::move(init.completion_handler)));
+ return init.result.get();
+ }
+
+ static RADOS make_with_librados(librados::Rados& rados);
+
+ RADOS(const RADOS&) = delete;
+ RADOS& operator =(const RADOS&) = delete;
+
+ RADOS(RADOS&&);
+ RADOS& operator =(RADOS&&);
+
+ ~RADOS();
+
+ CephContext* cct();
+
+ using executor_type = boost::asio::io_context::executor_type;
+ executor_type get_executor() const;
+ boost::asio::io_context& get_io_context();
+
+ template<typename CompletionToken>
+ auto execute(const Object& o, const IOContext& ioc, ReadOp&& op,
+ ceph::buffer::list* bl,
+ CompletionToken&& token, uint64_t* objver = nullptr,
+ const blkin_trace_info* trace_info = nullptr) {
+ boost::asio::async_completion<CompletionToken, Op::Signature> init(token);
+ execute(o, ioc, std::move(op), bl,
+ ReadOp::Completion::create(get_executor(),
+ std::move(init.completion_handler)),
+ objver, trace_info);
+ return init.result.get();
+ }
+
+ template<typename CompletionToken>
+ auto execute(const Object& o, const IOContext& ioc, WriteOp&& op,
+ CompletionToken&& token, uint64_t* objver = nullptr,
+ const blkin_trace_info* trace_info = nullptr) {
+ boost::asio::async_completion<CompletionToken, Op::Signature> init(token);
+ execute(o, ioc, std::move(op),
+ Op::Completion::create(get_executor(),
+ std::move(init.completion_handler)),
+ objver, trace_info);
+ return init.result.get();
+ }
+
+ template<typename CompletionToken>
+ auto execute(const Object& o, std::int64_t pool,
+ ReadOp&& op,
+ ceph::buffer::list* bl,
+ CompletionToken&& token,
+ std::optional<std::string_view> ns = {},
+ std::optional<std::string_view> key = {},
+ uint64_t* objver = nullptr) {
+ boost::asio::async_completion<CompletionToken, Op::Signature> init(token);
+ execute(o, pool, std::move(op), bl,
+ ReadOp::Completion::create(get_executor(),
+ std::move(init.completion_handler)),
+ ns, key, objver);
+ return init.result.get();
+ }
+
+ template<typename CompletionToken>
+ auto execute(const Object& o, std::int64_t pool, WriteOp&& op,
+ CompletionToken&& token,
+ std::optional<std::string_view> ns = {},
+ std::optional<std::string_view> key = {},
+ uint64_t* objver = nullptr) {
+ boost::asio::async_completion<CompletionToken, Op::Signature> init(token);
+ execute(o, pool, std::move(op),
+ Op::Completion::create(get_executor(),
+ std::move(init.completion_handler)),
+ ns, key, objver);
+ return init.result.get();
+ }
+
+ boost::uuids::uuid get_fsid() const noexcept;
+
+ using LookupPoolSig = void(boost::system::error_code,
+ std::int64_t);
+ using LookupPoolComp = ceph::async::Completion<LookupPoolSig>;
+ template<typename CompletionToken>
+ auto lookup_pool(std::string_view name,
+ CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, LookupPoolSig> init(token);
+ lookup_pool(name,
+ LookupPoolComp::create(get_executor(),
+ std::move(init.completion_handler)));
+ return init.result.get();
+ }
+
+ std::optional<uint64_t> get_pool_alignment(int64_t pool_id);
+
+ using LSPoolsSig = void(std::vector<std::pair<std::int64_t, std::string>>);
+ using LSPoolsComp = ceph::async::Completion<LSPoolsSig>;
+ template<typename CompletionToken>
+ auto list_pools(CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, LSPoolsSig> init(token);
+ list_pools(LSPoolsComp::create(get_executor(),
+ std::move(init.completion_handler)));
+ return init.result.get();
+ }
+
+
+
+ using SimpleOpSig = void(boost::system::error_code);
+ using SimpleOpComp = ceph::async::Completion<SimpleOpSig>;
+ template<typename CompletionToken>
+ auto create_pool_snap(int64_t pool, std::string_view snapName,
+ CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, SimpleOpSig> init(token);
+ create_pool_snap(pool, snapName,
+ SimpleOpComp::create(get_executor(),
+ std::move(init.completion_handler)));
+ return init.result.get();
+ }
+
+ using SMSnapSig = void(boost::system::error_code, std::uint64_t);
+ using SMSnapComp = ceph::async::Completion<SMSnapSig>;
+ template<typename CompletionToken>
+ auto allocate_selfmanaged_snap(int64_t pool,
+ CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, SMSnapSig> init(token);
+ allocate_selfmanaged_snap(pool,
+ SMSnapComp::create(
+ get_executor(),
+ std::move(init.completion_handler)));
+ return init.result.get();
+ }
+
+ template<typename CompletionToken>
+ auto delete_pool_snap(int64_t pool, std::string_view snapName,
+ CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, SimpleOpSig> init(token);
+ delete_pool_snap(pool, snapName,
+ SimpleOpComp::create(get_executor(),
+ std::move(init.completion_handler)));
+ return init.result.get();
+ }
+
+ template<typename CompletionToken>
+ auto delete_selfmanaged_snap(int64_t pool, std::string_view snapName,
+ CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, SimpleOpSig> init(token);
+ delete_selfmanaged_snap(pool, snapName,
+ SimpleOpComp::create(
+ get_executor(),
+ std::move(init.completion_handler)));
+ return init.result.get();
+ }
+
+ template<typename CompletionToken>
+ auto create_pool(std::string_view name, std::optional<int> crush_rule,
+ CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, SimpleOpSig> init(token);
+ create_pool(name, crush_rule,
+ SimpleOpComp::create(get_executor(),
+ std::move(init.completion_handler)));
+ return init.result.get();
+ }
+
+ template<typename CompletionToken>
+ auto delete_pool(std::string_view name,
+ CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, SimpleOpSig> init(token);
+ delete_pool(name,
+ SimpleOpComp::create(get_executor(),
+ std::move(init.completion_handler)));
+ return init.result.get();
+ }
+
+ template<typename CompletionToken>
+ auto delete_pool(int64_t pool,
+ CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, SimpleOpSig> init(token);
+ delete_pool(pool,
+ SimpleOpComp::create(get_executor(),
+ std::move(init.completion_handler)));
+ return init.result.get();
+ }
+
+ using PoolStatSig = void(boost::system::error_code,
+ boost::container::flat_map<std::string,
+ PoolStats>, bool);
+ using PoolStatComp = ceph::async::Completion<PoolStatSig>;
+ template<typename CompletionToken>
+ auto stat_pools(const std::vector<std::string>& pools,
+ CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, PoolStatSig> init(token);
+ stat_pools(pools,
+ PoolStatComp::create(get_executor(),
+ std::move(init.completion_handler)));
+ return init.result.get();
+ }
+
+ using StatFSSig = void(boost::system::error_code,
+ FSStats);
+ using StatFSComp = ceph::async::Completion<StatFSSig>;
+ template<typename CompletionToken>
+ auto statfs(std::optional<int64_t> pool,
+ CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, StatFSSig> init(token);
+ ceph_statfs(pool, StatFSComp::create(get_executor(),
+ std::move(init.completion_handler)));
+ return init.result.get();
+ }
+
+ using WatchCB = fu2::unique_function<void(boost::system::error_code,
+ uint64_t notify_id,
+ uint64_t cookie,
+ uint64_t notifier_id,
+ ceph::buffer::list&& bl)>;
+
+ using WatchSig = void(boost::system::error_code ec,
+ uint64_t cookie);
+ using WatchComp = ceph::async::Completion<WatchSig>;
+ template<typename CompletionToken>
+ auto watch(const Object& o, const IOContext& ioc,
+ std::optional<std::chrono::seconds> timeout,
+ WatchCB&& cb, CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, WatchSig> init(token);
+ watch(o, ioc, timeout, std::move(cb),
+ WatchComp::create(get_executor(),
+ std::move(init.completion_handler)));
+ return init.result.get();
+ }
+
+ template<typename CompletionToken>
+ auto watch(const Object& o, std::int64_t pool,
+ std::optional<std::chrono::seconds> timeout,
+ WatchCB&& cb, CompletionToken&& token,
+ std::optional<std::string_view> ns = {},
+ std::optional<std::string_view> key = {}) {
+ boost::asio::async_completion<CompletionToken, WatchSig> init(token);
+ watch(o, pool, timeout, std::move(cb),
+ WatchComp::create(get_executor(),
+ std::move(init.completion_handler)),
+ ns, key);
+ return init.result.get();
+ }
+
+ template<typename CompletionToken>
+ auto notify_ack(const Object& o,
+ const IOContext& ioc,
+ uint64_t notify_id,
+ uint64_t cookie,
+ ceph::buffer::list&& bl,
+ CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, SimpleOpSig> init(token);
+ notify_ack(o, ioc, notify_id, cookie, std::move(bl),
+ SimpleOpComp::create(get_executor(),
+ std::move(init.completion_handler)));
+ return init.result.get();
+ }
+
+ template<typename CompletionToken>
+ auto notify_ack(const Object& o,
+ std::int64_t pool,
+ uint64_t notify_id,
+ uint64_t cookie,
+ ceph::buffer::list&& bl,
+ CompletionToken&& token,
+ std::optional<std::string_view> ns = {},
+ std::optional<std::string_view> key = {}) {
+ boost::asio::async_completion<CompletionToken, WatchSig> init(token);
+ notify_ack(o, pool, notify_id, cookie, std::move(bl),
+ SimpleOpComp::create(get_executor(),
+ std::move(init.completion_handler)),
+ ns, key);
+ return init.result.get();
+ }
+
+ template<typename CompletionToken>
+ auto unwatch(uint64_t cookie, const IOContext& ioc,
+ CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, SimpleOpSig> init(token);
+ unwatch(cookie, ioc,
+ SimpleOpComp::create(get_executor(),
+ std::move(init.completion_handler)));
+ return init.result.get();
+ }
+
+ template<typename CompletionToken>
+ auto unwatch(uint64_t cookie, std::int64_t pool,
+ CompletionToken&& token,
+ std::optional<std::string_view> ns = {},
+ std::optional<std::string_view> key = {}) {
+ boost::asio::async_completion<CompletionToken, SimpleOpSig> init(token);
+ unwatch(cookie, pool,
+ SimpleOpComp::create(get_executor(),
+ std::move(init.completion_handler)),
+ ns, key);
+ return init.result.get();
+ }
+
+ // This is one of those places where having to force everything into
+ // a .cc file is really infuriating. If we had modules, that would
+ // let us separate out the implementation details without
+ // sacrificing all the benefits of templates.
+ using VoidOpSig = void();
+ using VoidOpComp = ceph::async::Completion<VoidOpSig>;
+ template<typename CompletionToken>
+ auto flush_watch(CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, VoidOpSig> init(token);
+ flush_watch(VoidOpComp::create(get_executor(),
+ std::move(init.completion_handler)));
+ return init.result.get();
+ }
+
+ using NotifySig = void(boost::system::error_code, ceph::buffer::list);
+ using NotifyComp = ceph::async::Completion<NotifySig>;
+ template<typename CompletionToken>
+ auto notify(const Object& oid, const IOContext& ioc, ceph::buffer::list&& bl,
+ std::optional<std::chrono::milliseconds> timeout,
+ CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, NotifySig> init(token);
+ notify(oid, ioc, std::move(bl), timeout,
+ NotifyComp::create(get_executor(),
+ std::move(init.completion_handler)));
+
+ return init.result.get();
+ }
+
+ template<typename CompletionToken>
+ auto notify(const Object& oid, std::int64_t pool, ceph::buffer::list&& bl,
+ std::optional<std::chrono::milliseconds> timeout,
+ CompletionToken&& token,
+ std::optional<std::string_view> ns = {},
+ std::optional<std::string_view> key = {}) {
+ boost::asio::async_completion<CompletionToken, NotifySig> init(token);
+ notify(oid, pool, bl, timeout,
+ NotifyComp::create(get_executor(),
+ std::move(init.completion_handler)),
+ ns, key);
+
+ return init.result.get();
+ }
+
+ // The versions with pointers are fine for coroutines, but
+ // extraordinarily unappealing for callback-oriented programming.
+ using EnumerateSig = void(boost::system::error_code,
+ std::vector<Entry>,
+ Cursor);
+ using EnumerateComp = ceph::async::Completion<EnumerateSig>;
+ template<typename CompletionToken>
+ auto enumerate_objects(const IOContext& ioc, const Cursor& begin,
+ const Cursor& end, const std::uint32_t max,
+ const ceph::buffer::list& filter,
+ CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, EnumerateSig> init(token);
+ enumerate_objects(ioc, begin, end, max, filter,
+ EnumerateComp::create(get_executor(),
+ std::move(init.completion_handler)));
+ return init.result.get();
+ }
+
+ template<typename CompletionToken>
+ auto enumerate_objects(std::int64_t pool, const Cursor& begin,
+ const Cursor& end, const std::uint32_t max,
+ const ceph::buffer::list& filter,
+ CompletionToken&& token,
+ std::optional<std::string_view> ns = {},
+ std::optional<std::string_view> key = {}) {
+ boost::asio::async_completion<CompletionToken, EnumerateSig> init(token);
+ enumerate_objects(pool, begin, end, max, filter,
+ EnumerateComp::create(get_executor(),
+ std::move(init.completion_handler)),
+ ns, key);
+ return init.result.get();
+ }
+
+ using CommandSig = void(boost::system::error_code,
+ std::string, ceph::buffer::list);
+ using CommandComp = ceph::async::Completion<CommandSig>;
+ template<typename CompletionToken>
+ auto osd_command(int osd, std::vector<std::string>&& cmd,
+ ceph::buffer::list&& in, CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, CommandSig> init(token);
+ osd_command(osd, std::move(cmd), std::move(in),
+ CommandComp::create(get_executor(),
+ std::move(init.completion_handler)));
+ return init.result.get();
+ }
+ template<typename CompletionToken>
+ auto pg_command(PG pg, std::vector<std::string>&& cmd,
+ ceph::buffer::list&& in, CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, CommandSig> init(token);
+ pg_command(pg, std::move(cmd), std::move(in),
+ CommandComp::create(get_executor(),
+ std::move(init.completion_handler)));
+ return init.result.get();
+ }
+
+ template<typename CompletionToken>
+ auto mon_command(std::vector<std::string> command,
+ const ceph::buffer::list& bl,
+ std::string* outs, ceph::buffer::list* outbl,
+ CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, SimpleOpSig> init(token);
+ mon_command(command, bl, outs, outbl,
+ SimpleOpComp::create(get_executor(),
+ std::move(init.completion_handler)));
+ return init.result.get();
+ }
+
+ template<typename CompletionToken>
+ auto enable_application(std::string_view pool, std::string_view app_name,
+ bool force, CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, SimpleOpSig> init(token);
+ enable_application(pool, app_name, force,
+ SimpleOpComp::create(get_executor(),
+ std::move(init.completion_handler)));
+ return init.result.get();
+ }
+
+ template<typename CompletionToken>
+ auto blocklist_add(std::string_view client_address,
+ std::optional<std::chrono::seconds> expire,
+ CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, SimpleOpSig> init(token);
+ blocklist_add(client_address, expire,
+ SimpleOpComp::create(get_executor(),
+ std::move(init.completion_handler)));
+ return init.result.get();
+ }
+
+ template<typename CompletionToken>
+ auto wait_for_latest_osd_map(CompletionToken&& token) {
+ boost::asio::async_completion<CompletionToken, SimpleOpSig> init(token);
+ wait_for_latest_osd_map(
+ SimpleOpComp::create(get_executor(), std::move(init.completion_handler)));
+ return init.result.get();
+ }
+
+ uint64_t instance_id() const;
+
+private:
+
+ RADOS();
+
+ friend Builder;
+
+ RADOS(std::unique_ptr<detail::Client> impl);
+ static void make_with_cct(CephContext* cct,
+ boost::asio::io_context& ioctx,
+ std::unique_ptr<BuildComp> c);
+
+ void execute(const Object& o, const IOContext& ioc, ReadOp&& op,
+ ceph::buffer::list* bl, std::unique_ptr<Op::Completion> c,
+ uint64_t* objver, const blkin_trace_info* trace_info);
+
+ void execute(const Object& o, const IOContext& ioc, WriteOp&& op,
+ std::unique_ptr<Op::Completion> c, uint64_t* objver,
+ const blkin_trace_info* trace_info);
+
+ void execute(const Object& o, std::int64_t pool, ReadOp&& op,
+ ceph::buffer::list* bl, std::unique_ptr<Op::Completion> c,
+ std::optional<std::string_view> ns,
+ std::optional<std::string_view> key,
+ uint64_t* objver);
+
+ void execute(const Object& o, std::int64_t pool, WriteOp&& op,
+ std::unique_ptr<Op::Completion> c,
+ std::optional<std::string_view> ns,
+ std::optional<std::string_view> key,
+ uint64_t* objver);
+
+ void lookup_pool(std::string_view name, std::unique_ptr<LookupPoolComp> c);
+ void list_pools(std::unique_ptr<LSPoolsComp> c);
+ void create_pool_snap(int64_t pool, std::string_view snapName,
+ std::unique_ptr<SimpleOpComp> c);
+ void allocate_selfmanaged_snap(int64_t pool, std::unique_ptr<SMSnapComp> c);
+ void delete_pool_snap(int64_t pool, std::string_view snapName,
+ std::unique_ptr<SimpleOpComp> c);
+ void delete_selfmanaged_snap(int64_t pool, std::uint64_t snap,
+ std::unique_ptr<SimpleOpComp> c);
+ void create_pool(std::string_view name, std::optional<int> crush_rule,
+ std::unique_ptr<SimpleOpComp> c);
+ void delete_pool(std::string_view name,
+ std::unique_ptr<SimpleOpComp> c);
+ void delete_pool(int64_t pool,
+ std::unique_ptr<SimpleOpComp> c);
+ void stat_pools(const std::vector<std::string>& pools,
+ std::unique_ptr<PoolStatComp> c);
+ void stat_fs(std::optional<std::int64_t> pool,
+ std::unique_ptr<StatFSComp> c);
+
+ void watch(const Object& o, const IOContext& ioc,
+ std::optional<std::chrono::seconds> timeout,
+ WatchCB&& cb, std::unique_ptr<WatchComp> c);
+ void watch(const Object& o, std::int64_t pool,
+ std::optional<std::chrono::seconds> timeout,
+ WatchCB&& cb, std::unique_ptr<WatchComp> c,
+ std::optional<std::string_view> ns,
+ std::optional<std::string_view> key);
+ tl::expected<ceph::timespan, boost::system::error_code>
+ watch_check(uint64_t cookie);
+ void notify_ack(const Object& o,
+ const IOContext& _ioc,
+ uint64_t notify_id,
+ uint64_t cookie,
+ ceph::buffer::list&& bl,
+ std::unique_ptr<SimpleOpComp>);
+ void notify_ack(const Object& o,
+ std::int64_t pool,
+ uint64_t notify_id,
+ uint64_t cookie,
+ ceph::buffer::list&& bl,
+ std::unique_ptr<SimpleOpComp>,
+ std::optional<std::string_view> ns,
+ std::optional<std::string_view> key);
+ void unwatch(uint64_t cookie, const IOContext& ioc,
+ std::unique_ptr<SimpleOpComp>);
+ void unwatch(uint64_t cookie, std::int64_t pool,
+ std::unique_ptr<SimpleOpComp>,
+ std::optional<std::string_view> ns,
+ std::optional<std::string_view> key);
+ void notify(const Object& oid, const IOContext& ioctx,
+ ceph::buffer::list&& bl,
+ std::optional<std::chrono::milliseconds> timeout,
+ std::unique_ptr<NotifyComp> c);
+ void notify(const Object& oid, std::int64_t pool,
+ ceph::buffer::list&& bl,
+ std::optional<std::chrono::milliseconds> timeout,
+ std::unique_ptr<NotifyComp> c,
+ std::optional<std::string_view> ns,
+ std::optional<std::string_view> key);
+ void flush_watch(std::unique_ptr<VoidOpComp>);
+
+ void enumerate_objects(const IOContext& ioc, const Cursor& begin,
+ const Cursor& end, const std::uint32_t max,
+ const ceph::buffer::list& filter,
+ std::vector<Entry>* ls,
+ Cursor* cursor,
+ std::unique_ptr<SimpleOpComp> c);
+ void enumerate_objects(std::int64_t pool, const Cursor& begin,
+ const Cursor& end, const std::uint32_t max,
+ const ceph::buffer::list& filter,
+ std::vector<Entry>* ls,
+ Cursor* cursor,
+ std::unique_ptr<SimpleOpComp> c,
+ std::optional<std::string_view> ns,
+ std::optional<std::string_view> key);
+ void enumerate_objects(const IOContext& ioc, const Cursor& begin,
+ const Cursor& end, const std::uint32_t max,
+ const ceph::buffer::list& filter,
+ std::unique_ptr<EnumerateComp> c);
+ void enumerate_objects(std::int64_t pool, const Cursor& begin,
+ const Cursor& end, const std::uint32_t max,
+ const ceph::buffer::list& filter,
+ std::unique_ptr<EnumerateComp> c,
+ std::optional<std::string_view> ns,
+ std::optional<std::string_view> key);
+ void osd_command(int osd, std::vector<std::string>&& cmd,
+ ceph::buffer::list&& in, std::unique_ptr<CommandComp> c);
+ void pg_command(PG pg, std::vector<std::string>&& cmd,
+ ceph::buffer::list&& in, std::unique_ptr<CommandComp> c);
+
+ void mon_command(std::vector<std::string> command,
+ const ceph::buffer::list& bl,
+ std::string* outs, ceph::buffer::list* outbl,
+ std::unique_ptr<SimpleOpComp> c);
+
+ void enable_application(std::string_view pool, std::string_view app_name,
+ bool force, std::unique_ptr<SimpleOpComp> c);
+
+ void blocklist_add(std::string_view client_address,
+ std::optional<std::chrono::seconds> expire,
+ std::unique_ptr<SimpleOpComp> c);
+
+ void wait_for_latest_osd_map(std::unique_ptr<SimpleOpComp> c);
+
+ // Proxy object to provide access to low-level RADOS messaging clients
+ std::unique_ptr<detail::Client> impl;
+};
+
+enum class errc {
+ pool_dne = 1,
+ invalid_snapcontext
+};
+
+const boost::system::error_category& error_category() noexcept;
+}
+
+namespace boost::system {
+template<>
+struct is_error_code_enum<::neorados::errc> {
+ static const bool value = true;
+};
+
+template<>
+struct is_error_condition_enum<::neorados::errc> {
+ static const bool value = false;
+};
+}
+
+namespace neorados {
+// explicit conversion:
+inline boost::system::error_code make_error_code(errc e) noexcept {
+ return { static_cast<int>(e), error_category() };
+}
+
+// implicit conversion:
+inline boost::system::error_condition make_error_condition(errc e) noexcept {
+ return { static_cast<int>(e), error_category() };
+}
+}
+
+namespace std {
+template<>
+struct hash<neorados::Object> {
+ size_t operator ()(const neorados::Object& r) const;
+};
+template<>
+struct hash<neorados::IOContext> {
+ size_t operator ()(const neorados::IOContext& r) const;
+};
+} // namespace std
+
+#endif // NEORADOS_RADOS_HPP
diff --git a/src/include/neorados/RADOS_Decodable.hpp b/src/include/neorados/RADOS_Decodable.hpp
new file mode 100644
index 000000000..9654a8489
--- /dev/null
+++ b/src/include/neorados/RADOS_Decodable.hpp
@@ -0,0 +1,107 @@
+// -*- 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) 2020 Red Hat <contact@redhat.com>
+ * Author: Adam C. Emerson
+ *
+ * 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.
+ *
+ */
+
+#ifndef NEORADOS_RADOS_DECODABLE_HPP
+#define NEORADOS_RADOS_DECODABLE_HPP
+
+#include <cstdint>
+#include <cstdlib>
+#include <string>
+#include <iostream>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+namespace neorados {
+struct Entry {
+ std::string nspace;
+ std::string oid;
+ std::string locator;
+
+ Entry() {}
+ Entry(std::string nspace, std::string oid, std::string locator) :
+ nspace(std::move(nspace)), oid(std::move(oid)), locator(locator) {}
+};
+inline bool operator ==(const Entry& l, const Entry r) {
+ return std::tie(l.nspace, l.oid, l.locator) ==
+ std::tie(r.nspace, r.oid, r.locator);
+}
+inline bool operator !=(const Entry& l, const Entry r) {
+ return std::tie(l.nspace, l.oid, l.locator) !=
+ std::tie(r.nspace, r.oid, r.locator);
+}
+inline bool operator <(const Entry& l, const Entry r) {
+ return std::tie(l.nspace, l.oid, l.locator) <
+ std::tie(r.nspace, r.oid, r.locator);
+}
+inline bool operator <=(const Entry& l, const Entry r) {
+ return std::tie(l.nspace, l.oid, l.locator) <=
+ std::tie(r.nspace, r.oid, r.locator);
+}
+inline bool operator >=(const Entry& l, const Entry r) {
+ return std::tie(l.nspace, l.oid, l.locator) >=
+ std::tie(r.nspace, r.oid, r.locator);
+}
+inline bool operator >(const Entry& l, const Entry r) {
+ return std::tie(l.nspace, l.oid, l.locator) >
+ std::tie(r.nspace, r.oid, r.locator);
+}
+
+inline std::ostream& operator <<(std::ostream& out, const Entry& entry) {
+ if (!entry.nspace.empty())
+ out << entry.nspace << '/';
+ out << entry.oid;
+ if (!entry.locator.empty())
+ out << '@' << entry.locator;
+ return out;
+}
+
+struct CloneInfo {
+ uint64_t cloneid = 0;
+ std::vector<uint64_t> snaps; // ascending
+ std::vector<std::pair<uint64_t, uint64_t>> overlap;// with next newest
+ uint64_t size = 0;
+ CloneInfo() = default;
+};
+
+struct SnapSet {
+ std::vector<CloneInfo> clones; // ascending
+ std::uint64_t seq = 0; // newest snapid seen by the object
+ SnapSet() = default;
+};
+
+struct ObjWatcher {
+ /// Address of the Watcher
+ std::string addr;
+ /// Watcher ID
+ std::int64_t watcher_id;
+ /// Cookie
+ std::uint64_t cookie;
+ /// Timeout in Seconds
+ std::uint32_t timeout_seconds;
+};
+}
+
+namespace std {
+template<>
+struct hash<::neorados::Entry> {
+ std::size_t operator ()(::neorados::Entry e) const {
+ hash<std::string> h;
+ return (h(e.nspace) << 2) ^ (h(e.oid) << 1) ^ h(e.locator);
+ }
+};
+}
+
+#endif // RADOS_DECODABLE_HPP
diff --git a/src/include/neorados/buffer_fwd.h b/src/include/neorados/buffer_fwd.h
new file mode 120000
index 000000000..bd1f6f1b0
--- /dev/null
+++ b/src/include/neorados/buffer_fwd.h
@@ -0,0 +1 @@
+../buffer_fwd.h \ No newline at end of file
diff --git a/src/include/neorados/completion.h b/src/include/neorados/completion.h
new file mode 120000
index 000000000..100678fc2
--- /dev/null
+++ b/src/include/neorados/completion.h
@@ -0,0 +1 @@
+../../common/async/completion.h \ No newline at end of file