diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:45:59 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:45:59 +0000 |
commit | 19fcec84d8d7d21e796c7624e521b60d28ee21ed (patch) | |
tree | 42d26aa27d1e3f7c0b8bd3fd14e7d7082f5008dc /src/include/neorados/RADOS.hpp | |
parent | Initial commit. (diff) | |
download | ceph-upstream/16.2.11+ds.tar.xz ceph-upstream/16.2.11+ds.zip |
Adding upstream version 16.2.11+ds.upstream/16.2.11+dsupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/include/neorados/RADOS.hpp')
-rw-r--r-- | src/include/neorados/RADOS.hpp | 1152 |
1 files changed, 1152 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 |