]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
librbd: new pool init/stat API methods
authorJason Dillaman <dillaman@redhat.com>
Tue, 30 Oct 2018 01:55:54 +0000 (21:55 -0400)
committerJason Dillaman <dillaman@redhat.com>
Fri, 9 Nov 2018 17:40:39 +0000 (12:40 -0500)
The init method is a stub for handling new pool initialization. It
currently only handles setting the application tag. The stats method
will quickly calculate the number of images and provisioned space for
those images within the pool. Querying the pool stats on a pool with
10,000 images only required approximately 2 seconds as compared to
over 2 minutes for a "rbd ls -l" scan.

Signed-off-by: Jason Dillaman <dillaman@redhat.com>
src/include/rbd/librbd.h
src/include/rbd/librbd.hpp
src/librbd/CMakeLists.txt
src/librbd/api/Pool.cc [new file with mode: 0644]
src/librbd/api/Pool.h [new file with mode: 0644]
src/librbd/librbd.cc
src/pybind/rbd/rbd.pyx
src/test/librbd/test_librbd.cc
src/test/librbd/test_support.cc
src/test/pybind/test_rbd.py

index 0bb5f7e86a51d29511ff1d1da5a1b7721a51ea94..8a4196cf6a1fed12b0e91d30123ded05dbe41ed7 100644 (file)
@@ -59,6 +59,7 @@ extern "C" {
 
 typedef void *rbd_image_t;
 typedef void *rbd_image_options_t;
+typedef void *rbd_pool_stats_t;
 
 typedef void *rbd_completion_t;
 typedef void (*rbd_callback_t)(rbd_completion_t cb, void *arg);
@@ -264,6 +265,17 @@ typedef struct {
   rbd_config_source_t source;
 } rbd_config_option_t;
 
+typedef enum {
+  RBD_POOL_STAT_OPTION_IMAGES,
+  RBD_POOL_STAT_OPTION_IMAGE_PROVISIONED_BYTES,
+  RBD_POOL_STAT_OPTION_IMAGE_MAX_PROVISIONED_BYTES,
+  RBD_POOL_STAT_OPTION_IMAGE_SNAPSHOTS,
+  RBD_POOL_STAT_OPTION_TRASH_IMAGES,
+  RBD_POOL_STAT_OPTION_TRASH_PROVISIONED_BYTES,
+  RBD_POOL_STAT_OPTION_TRASH_MAX_PROVISIONED_BYTES,
+  RBD_POOL_STAT_OPTION_TRASH_SNAPSHOTS
+} rbd_pool_stat_option_t;
+
 CEPH_RBD_API void rbd_image_options_create(rbd_image_options_t* opts);
 CEPH_RBD_API void rbd_image_options_destroy(rbd_image_options_t opts);
 CEPH_RBD_API int rbd_image_options_set_string(rbd_image_options_t opts,
@@ -1103,9 +1115,19 @@ CEPH_RBD_API int rbd_namespace_remove(rados_ioctx_t io,
                                       const char *namespace_name);
 CEPH_RBD_API int rbd_namespace_list(rados_ioctx_t io, char *namespace_names,
                                     size_t *size);
-CEPH_RBD_API int rbd_namespace_exists(rados_ioctx_t io, const char *namespace_name,
+CEPH_RBD_API int rbd_namespace_exists(rados_ioctx_t io,
+                                      const char *namespace_name,
                                       bool *exists);
 
+CEPH_RBD_API int rbd_pool_init(rados_ioctx_t io, bool force);
+
+CEPH_RBD_API void rbd_pool_stats_create(rbd_pool_stats_t *stats);
+CEPH_RBD_API void rbd_pool_stats_destroy(rbd_pool_stats_t stats);
+CEPH_RBD_API int rbd_pool_stats_option_add_uint64(rbd_pool_stats_t stats,
+                                                 int stat_option,
+                                                  uint64_t* stat_val);
+CEPH_RBD_API int rbd_pool_stats_get(rados_ioctx_t io, rbd_pool_stats_t stats);
+
 #ifdef __cplusplus
 }
 #endif
index f6a3d8be7ab3a9cb31138a046ea986ce4f0ef239..3b8d2cdffe87fc5adb9d2b60a102104022faceeb 100644 (file)
@@ -29,6 +29,7 @@ namespace librbd {
 
   class Image;
   class ImageOptions;
+  class PoolStats;
   typedef void *image_ctx_t;
   typedef void *completion_t;
   typedef void (*callback_t)(completion_t cb, void *arg);
@@ -290,6 +291,9 @@ public:
   int namespace_list(IoCtx& io_ctx, std::vector<std::string>* namespace_names);
   int namespace_exists(IoCtx& io_ctx, const char *namespace_name, bool *exists);
 
+  int pool_init(IoCtx& io_ctx, bool force);
+  int pool_stats_get(IoCtx& io_ctx, PoolStats *pool_stats);
+
   int pool_metadata_get(IoCtx &io_ctx, const std::string &key,
                         std::string *value);
   int pool_metadata_set(IoCtx &io_ctx, const std::string &key,
@@ -329,6 +333,22 @@ private:
   rbd_image_options_t opts;
 };
 
+class CEPH_RBD_API PoolStats {
+public:
+  PoolStats();
+  ~PoolStats();
+
+  PoolStats(const PoolStats&) = delete;
+  PoolStats& operator=(const PoolStats&) = delete;
+
+  int add(rbd_pool_stat_option_t option, uint64_t* opt_val);
+
+private:
+  friend class RBD;
+
+  rbd_pool_stats_t pool_stats;
+};
+
 class CEPH_RBD_API UpdateWatchCtx {
 public:
   virtual ~UpdateWatchCtx() {}
index 5501dc633c24190e09eaab6fdbc1cdf3b0f87b4d..38d625f10d316b7a92e3f1fd60e03bea0680dd27 100644 (file)
@@ -31,6 +31,7 @@ set(librbd_internal_srcs
   api/Migration.cc
   api/Mirror.cc
   api/Namespace.cc
+  api/Pool.cc
   api/PoolMetadata.cc
   api/Snapshot.cc
   api/Trash.cc
diff --git a/src/librbd/api/Pool.cc b/src/librbd/api/Pool.cc
new file mode 100644 (file)
index 0000000..6e36231
--- /dev/null
@@ -0,0 +1,354 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "librbd/api/Pool.h"
+#include "include/rados/librados.hpp"
+#include "common/dout.h"
+#include "common/errno.h"
+#include "common/Throttle.h"
+#include "cls/rbd/cls_rbd_client.h"
+#include "osd/osd_types.h"
+#include "librbd/Utils.h"
+#include "librbd/api/Config.h"
+#include "librbd/api/Image.h"
+#include "librbd/api/Trash.h"
+
+#define dout_subsys ceph_subsys_rbd
+
+namespace librbd {
+namespace api {
+
+namespace {
+
+#undef dout_prefix
+#define dout_prefix *_dout << "librbd::api::Pool::ImageStatRequest: " \
+                           << __func__ << " " << this << ": " \
+                           << "(id=" << m_image_id << "): "
+
+template <typename I>
+class ImageStatRequest {
+public:
+  ImageStatRequest(librados::IoCtx& io_ctx, SimpleThrottle& throttle,
+                   const std::string& image_id, bool scan_snaps,
+                   std::atomic<uint64_t>* bytes,
+                   std::atomic<uint64_t>* max_bytes,
+                   std::atomic<uint64_t>* snaps)
+    : m_cct(reinterpret_cast<CephContext*>(io_ctx.cct())),
+      m_io_ctx(io_ctx), m_throttle(throttle), m_image_id(image_id),
+      m_scan_snaps(scan_snaps), m_bytes(bytes), m_max_bytes(max_bytes),
+      m_snaps(snaps) {
+    m_throttle.start_op();
+  }
+
+  void send() {
+    get_head();
+  }
+
+protected:
+  void finish(int r) {
+    (*m_max_bytes) += m_max_size;
+    m_throttle.end_op(r);
+
+    delete this;
+  }
+
+private:
+  CephContext* m_cct;
+  librados::IoCtx& m_io_ctx;
+  SimpleThrottle& m_throttle;
+  const std::string& m_image_id;
+  bool m_scan_snaps;
+  std::atomic<uint64_t>* m_bytes;
+  std::atomic<uint64_t>* m_max_bytes;
+  std::atomic<uint64_t>* m_snaps;
+  bufferlist m_out_bl;
+
+  uint64_t m_max_size = 0;
+  ::SnapContext m_snapc;
+
+  void get_head() {
+    ldout(m_cct, 15) << dendl;
+
+    librados::ObjectReadOperation op;
+    cls_client::get_size_start(&op, CEPH_NOSNAP);
+    if (m_scan_snaps) {
+      cls_client::get_snapcontext_start(&op);
+    }
+
+    m_out_bl.clear();
+    auto aio_comp = util::create_rados_callback<
+      ImageStatRequest<I>, &ImageStatRequest<I>::handle_get_head>(this);
+    int r = m_io_ctx.aio_operate(util::header_name(m_image_id), aio_comp, &op,
+                                 &m_out_bl);
+    ceph_assert(r == 0);
+    aio_comp->release();
+  }
+
+  void handle_get_head(int r) {
+    ldout(m_cct, 15) << "r=" << r << dendl;
+
+    auto it = m_out_bl.cbegin();
+    if (r == 0) {
+      uint8_t order;
+      r = cls_client::get_size_finish(&it, &m_max_size, &order);
+      if (r == 0) {
+        (*m_bytes) += m_max_size;
+      }
+    }
+    if (m_scan_snaps && r == 0) {
+      r = cls_client::get_snapcontext_finish(&it, &m_snapc);
+      if (r == 0) {
+        (*m_snaps) += m_snapc.snaps.size();
+      }
+    }
+
+    if (r == -ENOENT) {
+      finish(r);
+      return;
+    } else if (r < 0) {
+      lderr(m_cct) << "failed to stat image: " << cpp_strerror(r) << dendl;
+      finish(r);
+      return;
+    }
+
+    if (!m_snapc.is_valid()) {
+      lderr(m_cct) << "snap context is invalid" << dendl;
+      finish(-EIO);
+      return;
+    }
+
+    get_snaps();
+  }
+
+  void get_snaps() {
+    if (!m_scan_snaps || m_snapc.snaps.empty()) {
+      finish(0);
+      return;
+    }
+
+    ldout(m_cct, 15) << dendl;
+    librados::ObjectReadOperation op;
+    for (auto snap_seq : m_snapc.snaps) {
+      cls_client::get_size_start(&op, snap_seq);
+    }
+
+    m_out_bl.clear();
+    auto aio_comp = util::create_rados_callback<
+      ImageStatRequest<I>, &ImageStatRequest<I>::handle_get_snaps>(this);
+    int r = m_io_ctx.aio_operate(util::header_name(m_image_id), aio_comp, &op,
+                                 &m_out_bl);
+    ceph_assert(r == 0);
+    aio_comp->release();
+  }
+
+  void handle_get_snaps(int r) {
+    ldout(m_cct, 15) << "r=" << r << dendl;
+
+    auto it = m_out_bl.cbegin();
+    for ([[maybe_unused]] auto snap_seq : m_snapc.snaps) {
+      uint64_t size;
+      if (r == 0) {
+        uint8_t order;
+        r = cls_client::get_size_finish(&it, &size, &order);
+      }
+      if (r == 0 && m_max_size < size) {
+        m_max_size = size;
+      }
+    }
+
+    if (r == -ENOENT) {
+      ldout(m_cct, 15) << "out-of-sync metadata" << dendl;
+      get_head();
+    } else if (r < 0) {
+      lderr(m_cct) << "failed to retrieve snap size: " << cpp_strerror(r)
+                   << dendl;
+      finish(r);
+    } else {
+      finish(0);
+    }
+  }
+
+};
+
+template <typename I>
+void get_pool_stat_option_value(typename Pool<I>::StatOptions* stat_options,
+                                rbd_pool_stat_option_t option,
+                                uint64_t** value) {
+  auto it = stat_options->find(option);
+  if (it == stat_options->end()) {
+    *value = nullptr;
+  } else {
+    *value = it->second;
+  }
+}
+
+template <typename I>
+int get_pool_stats(librados::IoCtx& io_ctx, const ConfigProxy& config,
+              const std::vector<std::string>& image_ids, uint64_t* image_count,
+              uint64_t* provisioned_bytes, uint64_t* max_provisioned_bytes,
+              uint64_t* snapshot_count) {
+
+  bool scan_snaps = ((max_provisioned_bytes != nullptr) ||
+                     (snapshot_count != nullptr));
+
+  SimpleThrottle throttle(
+    config.template get_val<uint64_t>("rbd_concurrent_management_ops"), true);
+  std::atomic<uint64_t> bytes{0};
+  std::atomic<uint64_t> max_bytes{0};
+  std::atomic<uint64_t> snaps{0};
+  for (auto& image_id : image_ids) {
+    if (throttle.pending_error()) {
+      break;
+    }
+
+    auto req = new ImageStatRequest<I>(io_ctx, throttle, image_id,
+                                       scan_snaps, &bytes, &max_bytes, &snaps);
+    req->send();
+  }
+
+  int r = throttle.wait_for_ret();
+  if (r < 0) {
+    return r;
+  }
+
+  if (image_count != nullptr) {
+    *image_count = image_ids.size();
+  }
+  if (provisioned_bytes != nullptr) {
+    *provisioned_bytes = bytes.load();
+  }
+  if (max_provisioned_bytes != nullptr) {
+    *max_provisioned_bytes = max_bytes.load();
+  }
+  if (snapshot_count != nullptr) {
+    *snapshot_count = snaps.load();
+  }
+
+  return 0;
+}
+
+} // anonymous namespace
+
+#undef dout_prefix
+#define dout_prefix *_dout << "librbd::api::Pool: " << __func__ << ": "
+
+template <typename I>
+int Pool<I>::init(librados::IoCtx& io_ctx, bool force) {
+  auto cct = reinterpret_cast<CephContext*>(io_ctx.cct());
+  ldout(cct, 10) << dendl;
+
+  int r = io_ctx.application_enable(pg_pool_t::APPLICATION_NAME_RBD, force);
+  if (r < 0) {
+    return r;
+  }
+
+  // TODO configure self-managed snapshots (and other one-time pool checks)
+
+  return 0;
+}
+
+template <typename I>
+int Pool<I>::add_stat_option(StatOptions* stat_options,
+                             rbd_pool_stat_option_t option,
+                             uint64_t* value) {
+  switch (option) {
+  case RBD_POOL_STAT_OPTION_IMAGES:
+  case RBD_POOL_STAT_OPTION_IMAGE_PROVISIONED_BYTES:
+  case RBD_POOL_STAT_OPTION_IMAGE_MAX_PROVISIONED_BYTES:
+  case RBD_POOL_STAT_OPTION_IMAGE_SNAPSHOTS:
+  case RBD_POOL_STAT_OPTION_TRASH_IMAGES:
+  case RBD_POOL_STAT_OPTION_TRASH_PROVISIONED_BYTES:
+  case RBD_POOL_STAT_OPTION_TRASH_MAX_PROVISIONED_BYTES:
+  case RBD_POOL_STAT_OPTION_TRASH_SNAPSHOTS:
+    stat_options->emplace(option, value);
+    return 0;
+  default:
+    break;
+  }
+  return -ENOENT;
+}
+
+template <typename I>
+int Pool<I>::get_stats(librados::IoCtx& io_ctx, StatOptions* stat_options) {
+  auto cct = reinterpret_cast<CephContext*>(io_ctx.cct());
+  ldout(cct, 10) << dendl;
+
+  ConfigProxy config{cct->_conf};
+  api::Config<I>::apply_pool_overrides(io_ctx, &config);
+
+  uint64_t* image_count;
+  uint64_t* provisioned_bytes;
+  uint64_t* max_provisioned_bytes;
+  uint64_t* snapshot_count;
+
+  get_pool_stat_option_value<I>(
+    stat_options, RBD_POOL_STAT_OPTION_IMAGES, &image_count);
+  get_pool_stat_option_value<I>(
+    stat_options, RBD_POOL_STAT_OPTION_IMAGE_PROVISIONED_BYTES,
+    &provisioned_bytes);
+  get_pool_stat_option_value<I>(
+    stat_options, RBD_POOL_STAT_OPTION_IMAGE_MAX_PROVISIONED_BYTES,
+    &max_provisioned_bytes);
+  get_pool_stat_option_value<I>(
+    stat_options, RBD_POOL_STAT_OPTION_IMAGE_SNAPSHOTS, &snapshot_count);
+  if (image_count != nullptr || provisioned_bytes != nullptr ||
+      max_provisioned_bytes != nullptr || snapshot_count != nullptr) {
+    typename Image<I>::ImageNameToIds images;
+    int r = Image<I>::list_images(io_ctx, &images);
+    if (r < 0) {
+      return r;
+    }
+
+    std::vector<std::string> image_ids;
+    image_ids.reserve(images.size());
+    for (auto& it : images) {
+      image_ids.push_back(std::move(it.second));
+    }
+
+    r = get_pool_stats<I>(io_ctx, config, image_ids, image_count,
+                          provisioned_bytes, max_provisioned_bytes,
+                          snapshot_count);
+    if (r < 0) {
+      return r;
+    }
+  }
+
+  get_pool_stat_option_value<I>(
+    stat_options, RBD_POOL_STAT_OPTION_TRASH_IMAGES, &image_count);
+  get_pool_stat_option_value<I>(
+    stat_options, RBD_POOL_STAT_OPTION_TRASH_PROVISIONED_BYTES,
+    &provisioned_bytes);
+  get_pool_stat_option_value<I>(
+    stat_options, RBD_POOL_STAT_OPTION_TRASH_MAX_PROVISIONED_BYTES,
+    &max_provisioned_bytes);
+  get_pool_stat_option_value<I>(
+    stat_options, RBD_POOL_STAT_OPTION_TRASH_SNAPSHOTS, &snapshot_count);
+  if (image_count != nullptr || provisioned_bytes != nullptr ||
+      max_provisioned_bytes != nullptr || snapshot_count != nullptr) {
+    std::vector<trash_image_info_t> trash_entries;
+    int r = Trash<I>::list(io_ctx, trash_entries);
+    if (r < 0 && r != -EOPNOTSUPP) {
+      return r;
+    }
+
+    std::vector<std::string> image_ids;
+    image_ids.reserve(trash_entries.size());
+    for (auto& it : trash_entries) {
+      image_ids.push_back(std::move(it.id));
+    }
+
+    r = get_pool_stats<I>(io_ctx, config, image_ids, image_count,
+                          provisioned_bytes, max_provisioned_bytes,
+                          snapshot_count);
+    if (r < 0) {
+      return r;
+    }
+  }
+
+  return 0;
+}
+
+} // namespace api
+} // namespace librbd
+
+template class librbd::api::Pool<librbd::ImageCtx>;
diff --git a/src/librbd/api/Pool.h b/src/librbd/api/Pool.h
new file mode 100644 (file)
index 0000000..e3b99a8
--- /dev/null
@@ -0,0 +1,39 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_LIBRBD_API_POOL_H
+#define CEPH_LIBRBD_API_POOL_H
+
+#include "include/int_types.h"
+#include "include/rbd/librbd.h"
+#include <map>
+
+namespace librados { class IoCtx; }
+
+namespace librbd {
+
+struct ImageCtx;
+
+namespace api {
+
+template <typename ImageCtxT = librbd::ImageCtx>
+class Pool {
+public:
+  typedef std::map<rbd_pool_stat_option_t, uint64_t*> StatOptions;
+
+  static int init(librados::IoCtx& io_ctx, bool force);
+
+  static int add_stat_option(StatOptions* stat_options,
+                             rbd_pool_stat_option_t option,
+                             uint64_t* value);
+
+  static int get_stats(librados::IoCtx& io_ctx, StatOptions* stat_options);
+
+};
+
+} // namespace api
+} // namespace librbd
+
+extern template class librbd::api::Pool<librbd::ImageCtx>;
+
+#endif // CEPH_LIBRBD_API_POOL_H
index 794084d852b65d0141e0598a560552f304c281d7..bd6c9e9e2b5efe56695ba2358ced408dd2c6ae83 100644 (file)
@@ -33,6 +33,7 @@
 #include "librbd/api/Migration.h"
 #include "librbd/api/Mirror.h"
 #include "librbd/api/Namespace.h"
+#include "librbd/api/Pool.h"
 #include "librbd/api/PoolMetadata.h"
 #include "librbd/api/Snapshot.h"
 #include "librbd/api/Trash.h"
@@ -284,8 +285,23 @@ namespace librbd {
   };
 
   /*
-    RBD
-  */
+   * Pool stats
+   */
+  PoolStats::PoolStats() {
+    rbd_pool_stats_create(&pool_stats);
+  }
+
+  PoolStats::~PoolStats() {
+    rbd_pool_stats_destroy(pool_stats);
+  }
+
+  int PoolStats::add(rbd_pool_stat_option_t option, uint64_t* opt_val) {
+    return rbd_pool_stats_option_add_uint64(pool_stats, option, opt_val);
+  }
+
+  /*
+   *  RBD
+   */
   RBD::RBD()
   {
   }
@@ -650,6 +666,16 @@ namespace librbd {
     return librbd::api::Namespace<>::exists(io_ctx, namespace_name, exists);
   }
 
+  int RBD::pool_init(IoCtx& io_ctx, bool force) {
+    return librbd::api::Pool<>::init(io_ctx, force);
+  }
+
+  int RBD::pool_stats_get(IoCtx& io_ctx, PoolStats* stats) {
+    auto pool_stat_options =
+      reinterpret_cast<librbd::api::Pool<>::StatOptions*>(stats->pool_stats);
+    return librbd::api::Pool<>::get_stats(io_ctx, pool_stat_options);
+  }
+
   int RBD::list(IoCtx& io_ctx, vector<string>& names)
   {
     TracepointProvider::initialize<tracepoint_traits>(get_cct(io_ctx));
@@ -3036,6 +3062,44 @@ extern "C" int rbd_namespace_exists(rados_ioctx_t io,
   return librbd::api::Namespace<>::exists(io_ctx, namespace_name, exists);
 }
 
+extern "C" int rbd_pool_init(rados_ioctx_t io, bool force) {
+  librados::IoCtx io_ctx;
+  librados::IoCtx::from_rados_ioctx_t(io, io_ctx);
+
+  return librbd::api::Pool<>::init(io_ctx, force);
+}
+
+extern "C" void rbd_pool_stats_create(rbd_pool_stats_t *stats) {
+  *stats = reinterpret_cast<rbd_pool_stats_t>(
+    new librbd::api::Pool<>::StatOptions{});
+}
+
+extern "C" void rbd_pool_stats_destroy(rbd_pool_stats_t stats) {
+  auto pool_stat_options =
+    reinterpret_cast<librbd::api::Pool<>::StatOptions*>(stats);
+  delete pool_stat_options;
+}
+
+extern "C" int rbd_pool_stats_option_add_uint64(rbd_pool_stats_t stats,
+                                                int stat_option,
+                                                uint64_t* stat_val) {
+  auto pool_stat_options =
+    reinterpret_cast<librbd::api::Pool<>::StatOptions*>(stats);
+  return librbd::api::Pool<>::add_stat_option(
+    pool_stat_options, static_cast<rbd_pool_stat_option_t>(stat_option),
+    stat_val);
+}
+
+extern "C" int rbd_pool_stats_get(
+    rados_ioctx_t io, rbd_pool_stats_t pool_stats) {
+  librados::IoCtx io_ctx;
+  librados::IoCtx::from_rados_ioctx_t(io, io_ctx);
+
+  auto pool_stat_options =
+    reinterpret_cast<librbd::api::Pool<>::StatOptions*>(pool_stats);
+  return librbd::api::Pool<>::get_stats(io_ctx, pool_stat_options);
+}
+
 extern "C" int rbd_copy(rbd_image_t image, rados_ioctx_t dest_p,
                        const char *destname)
 {
index f1c687e0d71992b986c6b0d0f4f84267fed0033e..2b2b909d4406ef3c23184943ce583c7fc5b61686 100644 (file)
@@ -87,6 +87,7 @@ cdef extern from "rbd/librbd.h" nogil:
     ctypedef void* rados_ioctx_t
     ctypedef void* rbd_image_t
     ctypedef void* rbd_image_options_t
+    ctypedef void* rbd_pool_stats_t
     ctypedef void *rbd_completion_t
 
     ctypedef struct rbd_image_info_t:
@@ -228,6 +229,16 @@ cdef extern from "rbd/librbd.h" nogil:
         char *value
         rbd_config_source_t source
 
+    ctypedef enum rbd_pool_stat_option_t:
+        _RBD_POOL_STAT_OPTION_IMAGES "RBD_POOL_STAT_OPTION_IMAGES"
+        _RBD_POOL_STAT_OPTION_IMAGE_PROVISIONED_BYTES "RBD_POOL_STAT_OPTION_IMAGE_PROVISIONED_BYTES"
+        _RBD_POOL_STAT_OPTION_IMAGE_MAX_PROVISIONED_BYTES "RBD_POOL_STAT_OPTION_IMAGE_MAX_PROVISIONED_BYTES"
+        _RBD_POOL_STAT_OPTION_IMAGE_SNAPSHOTS "RBD_POOL_STAT_OPTION_IMAGE_SNAPSHOTS"
+        _RBD_POOL_STAT_OPTION_TRASH_IMAGES "RBD_POOL_STAT_OPTION_TRASH_IMAGES"
+        _RBD_POOL_STAT_OPTION_TRASH_PROVISIONED_BYTES "RBD_POOL_STAT_OPTION_TRASH_PROVISIONED_BYTES"
+        _RBD_POOL_STAT_OPTION_TRASH_MAX_PROVISIONED_BYTES "RBD_POOL_STAT_OPTION_TRASH_MAX_PROVISIONED_BYTES"
+        _RBD_POOL_STAT_OPTION_TRASH_SNAPSHOTS "RBD_POOL_STAT_OPTION_TRASH_SNAPSHOTS"
+
     ctypedef void (*rbd_callback_t)(rbd_completion_t cb, void *arg)
     ctypedef int (*librbd_progress_fn_t)(uint64_t offset, uint64_t total, void* ptr)
 
@@ -536,6 +547,14 @@ cdef extern from "rbd/librbd.h" nogil:
     void rbd_config_image_list_cleanup(rbd_config_option_t *options,
                                        int max_options)
 
+    int rbd_pool_init(rados_ioctx_t, bint force)
+
+    void rbd_pool_stats_create(rbd_pool_stats_t *stats)
+    void rbd_pool_stats_destroy(rbd_pool_stats_t stats)
+    int rbd_pool_stats_option_add_uint64(rbd_pool_stats_t stats,
+                                         int stat_option, uint64_t* stat_val)
+    int rbd_pool_stats_get(rados_ioctx_t io, rbd_pool_stats_t stats)
+
 RBD_FEATURE_LAYERING = _RBD_FEATURE_LAYERING
 RBD_FEATURE_STRIPINGV2 = _RBD_FEATURE_STRIPINGV2
 RBD_FEATURE_EXCLUSIVE_LOCK = _RBD_FEATURE_EXCLUSIVE_LOCK
@@ -607,6 +626,16 @@ RBD_CONFIG_SOURCE_CONFIG = _RBD_CONFIG_SOURCE_CONFIG
 RBD_CONFIG_SOURCE_POOL = _RBD_CONFIG_SOURCE_POOL
 RBD_CONFIG_SOURCE_IMAGE = _RBD_CONFIG_SOURCE_IMAGE
 
+RBD_POOL_STAT_OPTION_IMAGES = _RBD_POOL_STAT_OPTION_IMAGES
+RBD_POOL_STAT_OPTION_IMAGE_PROVISIONED_BYTES = _RBD_POOL_STAT_OPTION_IMAGE_PROVISIONED_BYTES
+RBD_POOL_STAT_OPTION_IMAGE_MAX_PROVISIONED_BYTES = _RBD_POOL_STAT_OPTION_IMAGE_MAX_PROVISIONED_BYTES
+RBD_POOL_STAT_OPTION_IMAGE_SNAPSHOTS = _RBD_POOL_STAT_OPTION_IMAGE_SNAPSHOTS
+RBD_POOL_STAT_OPTION_TRASH_IMAGES = _RBD_POOL_STAT_OPTION_TRASH_IMAGES
+RBD_POOL_STAT_OPTION_TRASH_PROVISIONED_BYTES = _RBD_POOL_STAT_OPTION_TRASH_PROVISIONED_BYTES
+RBD_POOL_STAT_OPTION_TRASH_MAX_PROVISIONED_BYTES = _RBD_POOL_STAT_OPTION_TRASH_MAX_PROVISIONED_BYTES
+RBD_POOL_STAT_OPTION_TRASH_SNAPSHOTS = _RBD_POOL_STAT_OPTION_TRASH_SNAPSHOTS
+
+
 class Error(Exception):
     pass
 
@@ -1818,6 +1847,94 @@ class RBD(object):
         if ret != 0:
             raise make_ex(ret, 'error renaming group')
 
+    def pool_init(self, ioctx, force):
+        """
+        Initialize an RBD pool
+        :param ioctx: determines which RADOS pool
+        :type ioctx: :class:`rados.Ioctx`
+        :param force: force init
+        :type force: bool
+        """
+        cdef:
+            rados_ioctx_t _ioctx = convert_ioctx(ioctx)
+            bint _force = force
+        with nogil:
+            ret = rbd_pool_init(_ioctx, _force)
+        if ret != 0:
+            raise make_ex(ret, 'error initializing pool')
+
+    def pool_stats_get(self, ioctx):
+        """
+        Return RBD pool stats
+
+        :param ioctx: determines which RADOS pool
+        :type ioctx: :class:`rados.Ioctx`
+        :returns: dict - contains the following keys:
+
+            * ``image_count`` (int) - image count
+
+            * ``image_provisioned_bytes`` (int) - image total HEAD provisioned bytes
+
+            * ``image_max_provisioned_bytes`` (int) - image total max provisioned bytes
+
+            * ``image_snap_count`` (int) - image snap count
+
+            * ``trash_count`` (int) - trash image count
+
+            * ``trash_provisioned_bytes`` (int) - trash total HEAD provisioned bytes
+
+            * ``trash_max_provisioned_bytes`` (int) - trash total max provisioned bytes
+
+            * ``trash_snap_count`` (int) - trash snap count
+
+        """
+        cdef:
+            rados_ioctx_t _ioctx = convert_ioctx(ioctx)
+            uint64_t _image_count = 0
+            uint64_t _image_provisioned_bytes = 0
+            uint64_t _image_max_provisioned_bytes = 0
+            uint64_t _image_snap_count = 0
+            uint64_t _trash_count = 0
+            uint64_t _trash_provisioned_bytes = 0
+            uint64_t _trash_max_provisioned_bytes = 0
+            uint64_t _trash_snap_count = 0
+            rbd_pool_stats_t _stats
+
+        rbd_pool_stats_create(&_stats)
+        rbd_pool_stats_option_add_uint64(_stats, RBD_POOL_STAT_OPTION_IMAGES,
+                                         &_image_count)
+        rbd_pool_stats_option_add_uint64(_stats, RBD_POOL_STAT_OPTION_IMAGE_PROVISIONED_BYTES,
+                                         &_image_provisioned_bytes)
+        rbd_pool_stats_option_add_uint64(_stats, RBD_POOL_STAT_OPTION_IMAGE_MAX_PROVISIONED_BYTES,
+                                         &_image_max_provisioned_bytes)
+        rbd_pool_stats_option_add_uint64(_stats, RBD_POOL_STAT_OPTION_IMAGE_SNAPSHOTS,
+                                         &_image_snap_count)
+        rbd_pool_stats_option_add_uint64(_stats, RBD_POOL_STAT_OPTION_TRASH_IMAGES,
+                                         &_trash_count)
+        rbd_pool_stats_option_add_uint64(_stats, RBD_POOL_STAT_OPTION_TRASH_PROVISIONED_BYTES,
+                                         &_trash_provisioned_bytes)
+        rbd_pool_stats_option_add_uint64(_stats, RBD_POOL_STAT_OPTION_TRASH_MAX_PROVISIONED_BYTES,
+                                         &_trash_max_provisioned_bytes)
+        rbd_pool_stats_option_add_uint64(_stats, RBD_POOL_STAT_OPTION_TRASH_SNAPSHOTS,
+                                         &_trash_snap_count)
+        try:
+            with nogil:
+                ret = rbd_pool_stats_get(_ioctx, _stats)
+            if ret != 0:
+                raise make_ex(ret, 'error retrieving pool stats')
+        else:
+            return {'image_count': _image_count,
+                    'image_provisioned_bytes': _image_provisioned_bytes,
+                    'image_max_provisioned_bytes': _image_max_provisioned_bytes,
+                    'image_snap_count': _image_snap_count,
+                    'trash_count': _trash_count,
+                    'trash_provisioned_bytes': _trash_provisioned_bytes,
+                    'trash_max_provisioned_bytes': _trash_max_provisioned_bytes,
+                    'trash_snap_count': _trash_snap_count}
+        finally:
+            rbd_pool_stats_destroy(_stats)
+
+
 cdef class MirrorPeerIterator(object):
     """
     Iterator over mirror peer info for a pool.
index f77c5fdf682b20be3709c98b3aaf40d1169f5fd8..fd3373d4de406fccd1e87adcd8b5cd857c31dd7e 100644 (file)
@@ -7363,6 +7363,82 @@ TEST_F(TestLibRBD, ConfigPP)
   }
 }
 
+TEST_F(TestLibRBD, PoolStatsPP)
+{
+  REQUIRE_FORMAT_V2();
+
+  librados::IoCtx ioctx;
+  ASSERT_EQ(0, _rados.ioctx_create(create_pool(true).c_str(), ioctx));
+
+  librbd::RBD rbd;
+  std::string image_name;
+  uint64_t size = 2 << 20;
+  uint64_t expected_size = 0;
+  for (size_t idx = 0; idx < 4; ++idx) {
+    image_name = get_temp_image_name();
+
+    int order = 0;
+    ASSERT_EQ(0, create_image_pp(rbd, ioctx, image_name.c_str(), size, &order));
+    expected_size += size;
+  }
+
+  librbd::Image image;
+  ASSERT_EQ(0, rbd.open(ioctx, image, image_name.c_str(), NULL));
+  ASSERT_EQ(0, image.snap_create("snap1"));
+  ASSERT_EQ(0, image.resize(0));
+  ASSERT_EQ(0, image.close());
+  uint64_t expect_head_size = (expected_size - size);
+
+  uint64_t image_count;
+  uint64_t provisioned_bytes;
+  uint64_t max_provisioned_bytes;
+  uint64_t snap_count;
+  uint64_t trash_image_count;
+  uint64_t trash_provisioned_bytes;
+  uint64_t trash_max_provisioned_bytes;
+  uint64_t trash_snap_count;
+
+  librbd::PoolStats pool_stats1;
+  pool_stats1.add(RBD_POOL_STAT_OPTION_IMAGES, &image_count);
+  pool_stats1.add(RBD_POOL_STAT_OPTION_IMAGE_PROVISIONED_BYTES,
+                  &provisioned_bytes);
+  ASSERT_EQ(0, rbd.pool_stats_get(ioctx, &pool_stats1));
+
+  ASSERT_EQ(4U, image_count);
+  ASSERT_EQ(expect_head_size, provisioned_bytes);
+
+  pool_stats1.add(RBD_POOL_STAT_OPTION_IMAGE_MAX_PROVISIONED_BYTES,
+                  &max_provisioned_bytes);
+  ASSERT_EQ(0, rbd.pool_stats_get(ioctx, &pool_stats1));
+  ASSERT_EQ(4U, image_count);
+  ASSERT_EQ(expect_head_size, provisioned_bytes);
+  ASSERT_EQ(expected_size, max_provisioned_bytes);
+
+  librbd::PoolStats pool_stats2;
+  pool_stats2.add(RBD_POOL_STAT_OPTION_IMAGE_SNAPSHOTS, &snap_count);
+  pool_stats2.add(RBD_POOL_STAT_OPTION_TRASH_IMAGES, &trash_image_count);
+  pool_stats2.add(RBD_POOL_STAT_OPTION_TRASH_SNAPSHOTS, &trash_snap_count);
+  ASSERT_EQ(0, rbd.pool_stats_get(ioctx, &pool_stats2));
+  ASSERT_EQ(1U, snap_count);
+  ASSERT_EQ(0U, trash_image_count);
+  ASSERT_EQ(0U, trash_snap_count);
+
+  ASSERT_EQ(0, rbd.trash_move(ioctx, image_name.c_str(), 0));
+
+  librbd::PoolStats pool_stats3;
+  pool_stats3.add(RBD_POOL_STAT_OPTION_TRASH_IMAGES, &trash_image_count);
+  pool_stats3.add(RBD_POOL_STAT_OPTION_TRASH_PROVISIONED_BYTES,
+                  &trash_provisioned_bytes);
+  pool_stats3.add(RBD_POOL_STAT_OPTION_TRASH_MAX_PROVISIONED_BYTES,
+                  &trash_max_provisioned_bytes);
+  pool_stats3.add(RBD_POOL_STAT_OPTION_TRASH_SNAPSHOTS, &trash_snap_count);
+  ASSERT_EQ(0, rbd.pool_stats_get(ioctx, &pool_stats3));
+  ASSERT_EQ(1U, trash_image_count);
+  ASSERT_EQ(0U, trash_provisioned_bytes);
+  ASSERT_EQ(size, trash_max_provisioned_bytes);
+  ASSERT_EQ(1U, trash_snap_count);
+}
+
 // poorman's ceph_assert()
 namespace ceph {
   void __ceph_assert_fail(const char *assertion, const char *file, int line,
index b0eb932e9830aa03ee55bb83e71f2c683d48c755..e74068e6600a124b861967564106ef0e5507a5da 100644 (file)
@@ -115,9 +115,9 @@ int create_image_data_pool(librados::Rados &rados, std::string &data_pool, bool
   if (r < 0) {
     return r;
   }
-  ioctx.application_enable("rbd", true);
 
-  return r;
+  librbd::RBD rbd;
+  return rbd.pool_init(ioctx, true);
 }
 
 bool is_librados_test_stub(librados::Rados &rados) {
index 3b8ef8e29ee057d9abaacf5f14e12fddb064f9e9..f9f5131ccd7de27bfbd9fa3c69248d6676dd8831 100644 (file)
@@ -51,7 +51,7 @@ def setup_module():
     rados.create_pool(pool_name)
     global ioctx
     ioctx = rados.open_ioctx(pool_name)
-    ioctx.application_enable('rbd')
+    RBD().pool_init(ioctx, True)
     global features
     features = os.getenv("RBD_FEATURES")
     features = int(features) if features is not None else 61
@@ -92,6 +92,7 @@ def create_image():
                      features=int(features))
     else:
         RBD().create(ioctx, image_name, IMG_SIZE, IMG_ORDER, old_format=True)
+    return image_name
 
 def remove_image():
     if image_name is not None:
@@ -377,6 +378,36 @@ def test_config_list():
     for option in rbd.config_list(ioctx):
         eq(option['source'], RBD_CONFIG_SOURCE_CONFIG)
 
+@require_new_format()
+def test_pool_stats():
+    rbd = RBD()
+
+    try:
+        image1 = create_image()
+        image2 = create_image()
+        image3 = create_image()
+        image4 = create_image()
+        with Image(ioctx, image4) as image:
+            image.create_snap('snap')
+            image.resize(0)
+
+        stats = rbd.pool_stats_get(ioctx)
+        eq(stats['image_count'], 4)
+        eq(stats['image_provisioned_bytes'], 3 * IMG_SIZE)
+        eq(stats['image_max_provisioned_bytes'], 4 * IMG_SIZE)
+        eq(stats['image_snap_count'], 1)
+        eq(stats['trash_count'], 0)
+        eq(stats['trash_provisioned_bytes'], 0)
+        eq(stats['trash_max_provisioned_bytes'], 0)
+        eq(stats['trash_snap_count'], 0)
+    finally:
+        rbd.remove(ioctx, image1)
+        rbd.remove(ioctx, image2)
+        rbd.remove(ioctx, image3)
+        with Image(ioctx, image4) as image:
+            image.remove_snap('snap')
+        rbd.remove(ioctx, image4)
+
 def rand_data(size):
     return os.urandom(size)
 
@@ -1806,6 +1837,7 @@ class TestTrash(object):
 
         RBD().trash_move(ioctx, image_name, 1000)
         assert_raises(PermissionError, RBD().trash_remove, ioctx, image_id)
+        RBD().trash_remove(ioctx, image_id, True)
 
     def test_remove(self):
         create_image()