]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
neorados/cls: Helper function for reading CLS values
authorAdam C. Emerson <aemerson@redhat.com>
Sat, 21 Jan 2023 01:58:00 +0000 (20:58 -0500)
committerAdam C. Emerson <aemerson@redhat.com>
Tue, 1 Apr 2025 15:10:13 +0000 (11:10 -0400)
Since reading CLS values in a friendly way requires calling out to RADOS
then decoding the returned structure, make a function to help with that.

Signed-off-by: Adam C. Emerson <aemerson@redhat.com>
src/neorados/cls/common.h [new file with mode: 0644]

diff --git a/src/neorados/cls/common.h b/src/neorados/cls/common.h
new file mode 100644 (file)
index 0000000..475d239
--- /dev/null
@@ -0,0 +1,155 @@
+// -*- 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) 2023 IBM
+ *
+ * See file COPYING for license information.
+ *
+ */
+
+#pragma once
+
+#include <concepts>
+#include <coroutine>
+#include <cstddef>
+#include <string>
+#include <type_traits>
+#include <tuple>
+
+#include <boost/asio/async_result.hpp>
+#include <boost/asio/awaitable.hpp>
+#include <boost/asio/deferred.hpp>
+#include <boost/asio/detached.hpp>
+#include <boost/asio/io_context.hpp>
+#include <boost/asio/use_awaitable.hpp>
+
+#include <boost/asio/experimental/co_composed.hpp>
+
+#include <boost/system/error_code.hpp>
+#include <boost/system/system_error.hpp>
+
+#include "include/neorados/RADOS.hpp"
+
+#include "include/buffer.h"
+#include "include/encoding.h"
+
+/// \file neorados/cls/common.h
+///
+/// \brief Helpers for writing simple CLS clients
+///
+/// For writing functions that call out to the OSD, perform a CLS
+/// call, and return its data in a user friendly way, we need to
+/// coordinate asynchronous operation with decoding and returning the
+/// relevant data.
+///
+/// Ideally, these functions will help.
+
+namespace neorados::cls {
+namespace detail {
+template<typename...>
+struct return_sig;
+
+template<typename T>
+struct return_sig<T> {
+using type = void(boost::system::error_code, T);
+};
+template<typename ...Ts>
+struct return_sig<std::tuple<Ts...>> {
+  using type = void(boost::system::error_code, Ts...);
+};
+
+template<typename... Ts>
+using return_sig_t = typename return_sig<Ts...>::type;
+
+template<typename T>
+auto maybecat(boost::system::error_code ec,
+             T&& t)
+{
+  return std::make_tuple(ec, std::forward<T>(t));
+}
+
+template<typename ...Ts>
+auto maybecat(boost::system::error_code ec,
+             std::tuple<Ts...>&& ts)
+{
+  return std::tuple_cat(std::tuple(ec),
+                       std::move(ts));
+}
+} // namespace detail
+
+/// \brief Perform a CLS read operation and return the result
+///
+/// Asynchronously call into the OSD, decode its response and
+/// pass it to a supplied function that extracts the relevant
+/// information.
+///
+/// \tparam Rep The type of the CLS operation's result that will be
+///             passed to `f`
+///
+/// \param r RADOS handle
+/// \param oid Object name
+/// \param ioc IOContext locator
+/// \param cls Name of the object class
+/// \param method Object class method to call
+/// \param req Request (parameters to the class method). Pass nullptr if there
+///            is no request structure.
+/// \param f Function to extract/process call result
+/// \param token Boost.Asio CompletionToken
+///
+/// \return The relevant data in a way appropriate to the completion
+/// token. See Boost.Asio documentation. The signature is
+/// void(error_code, T) if f returns a non-tuple, and
+/// void(error_code, Ts...) if f returns a tuple.
+template<std::default_initializable Rep, typename Req,
+        std::invocable<Rep&&> F,
+        std::default_initializable Ret = std::invoke_result_t<F&&, Rep&&>,
+        boost::asio::completion_token_for<
+          detail::return_sig_t<Ret>> CompletionToken>
+auto exec(
+  RADOS& r,
+  Object oid,
+  IOContext ioc,
+  std::string cls,
+  std::string method,
+  const Req& req,
+  F&& f,
+  CompletionToken&& token)
+{
+  namespace asio = boost::asio;
+  namespace buffer = ceph::buffer;
+  using boost::system::error_code;
+  using boost::system::system_error;
+
+  buffer::list in;
+  if (!std::is_same_v<Req, std::nullptr_t>) {
+    encode(req, in);
+  }
+  return asio::async_initiate<CompletionToken, detail::return_sig_t<Ret>>
+    (asio::experimental::co_composed<detail::return_sig_t<Ret>>
+     ([](auto state, RADOS& r, Object oid, IOContext ioc, std::string cls,
+        std::string method, buffer::list in, F&& f) -> void {
+       try {
+        ReadOp op;
+        buffer::list out;
+        error_code ec;
+        op.exec(cls, method, std::move(in), &out, &ec);
+        co_await r.execute(std::move(oid), std::move(ioc), std::move(op),
+                           nullptr, asio::deferred);
+        if (ec) {
+          co_return detail::maybecat(ec, Ret{});
+        }
+        Rep rep;
+        decode(rep, out);
+        co_return detail::maybecat(error_code{},
+                                   std::invoke(std::forward<F>(f),
+                                               std::move(rep)));
+       } catch (const system_error& e) {
+        co_return detail::maybecat(e.code(), Ret{});
+       }
+     }, r.get_executor()),
+     token, std::ref(r), std::move(oid), std::move(ioc), std::move(cls),
+     std::move(method), std::move(in), std::forward<F>(f));
+}
+} // namespace neorados::cls