1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 #include "librbd/api/Pool.h"
5 #include "include/rados/librados.hpp"
6 #include "common/dout.h"
7 #include "common/errno.h"
8 #include "common/Cond.h"
9 #include "common/Throttle.h"
10 #include "cls/rbd/cls_rbd_client.h"
11 #include "osd/osd_types.h"
12 #include "librbd/ImageCtx.h"
13 #include "librbd/Utils.h"
14 #include "librbd/api/Config.h"
15 #include "librbd/api/Image.h"
16 #include "librbd/api/Trash.h"
17 #include "librbd/image/ValidatePoolRequest.h"
19 #define dout_subsys ceph_subsys_rbd
27 #define dout_prefix *_dout << "librbd::api::Pool::ImageStatRequest: " \
28 << __func__ << " " << this << ": " \
29 << "(id=" << m_image_id << "): "
32 class ImageStatRequest
{
34 ImageStatRequest(librados::IoCtx
& io_ctx
, SimpleThrottle
& throttle
,
35 const std::string
& image_id
, bool scan_snaps
,
36 std::atomic
<uint64_t>* bytes
,
37 std::atomic
<uint64_t>* max_bytes
,
38 std::atomic
<uint64_t>* snaps
)
39 : m_cct(reinterpret_cast<CephContext
*>(io_ctx
.cct())),
40 m_io_ctx(io_ctx
), m_throttle(throttle
), m_image_id(image_id
),
41 m_scan_snaps(scan_snaps
), m_bytes(bytes
), m_max_bytes(max_bytes
),
43 m_throttle
.start_op();
52 (*m_max_bytes
) += m_max_size
;
60 librados::IoCtx
& m_io_ctx
;
61 SimpleThrottle
& m_throttle
;
62 const std::string
& m_image_id
;
64 std::atomic
<uint64_t>* m_bytes
;
65 std::atomic
<uint64_t>* m_max_bytes
;
66 std::atomic
<uint64_t>* m_snaps
;
69 uint64_t m_max_size
= 0;
70 ::SnapContext m_snapc
;
73 ldout(m_cct
, 15) << dendl
;
75 librados::ObjectReadOperation op
;
76 cls_client::get_size_start(&op
, CEPH_NOSNAP
);
78 cls_client::get_snapcontext_start(&op
);
82 auto aio_comp
= util::create_rados_callback
<
83 ImageStatRequest
<I
>, &ImageStatRequest
<I
>::handle_get_head
>(this);
84 int r
= m_io_ctx
.aio_operate(util::header_name(m_image_id
), aio_comp
, &op
,
90 void handle_get_head(int r
) {
91 ldout(m_cct
, 15) << "r=" << r
<< dendl
;
93 auto it
= m_out_bl
.cbegin();
96 r
= cls_client::get_size_finish(&it
, &m_max_size
, &order
);
98 (*m_bytes
) += m_max_size
;
101 if (m_scan_snaps
&& r
== 0) {
102 r
= cls_client::get_snapcontext_finish(&it
, &m_snapc
);
104 (*m_snaps
) += m_snapc
.snaps
.size();
112 lderr(m_cct
) << "failed to stat image: " << cpp_strerror(r
) << dendl
;
117 if (!m_snapc
.is_valid()) {
118 lderr(m_cct
) << "snap context is invalid" << dendl
;
127 if (!m_scan_snaps
|| m_snapc
.snaps
.empty()) {
132 ldout(m_cct
, 15) << dendl
;
133 librados::ObjectReadOperation op
;
134 for (auto snap_seq
: m_snapc
.snaps
) {
135 cls_client::get_size_start(&op
, snap_seq
);
139 auto aio_comp
= util::create_rados_callback
<
140 ImageStatRequest
<I
>, &ImageStatRequest
<I
>::handle_get_snaps
>(this);
141 int r
= m_io_ctx
.aio_operate(util::header_name(m_image_id
), aio_comp
, &op
,
147 void handle_get_snaps(int r
) {
148 ldout(m_cct
, 15) << "r=" << r
<< dendl
;
150 auto it
= m_out_bl
.cbegin();
151 for ([[maybe_unused
]] auto snap_seq
: m_snapc
.snaps
) {
155 r
= cls_client::get_size_finish(&it
, &size
, &order
);
157 if (r
== 0 && m_max_size
< size
) {
163 ldout(m_cct
, 15) << "out-of-sync metadata" << dendl
;
166 lderr(m_cct
) << "failed to retrieve snap size: " << cpp_strerror(r
)
176 template <typename I
>
177 void get_pool_stat_option_value(typename Pool
<I
>::StatOptions
* stat_options
,
178 rbd_pool_stat_option_t option
,
180 auto it
= stat_options
->find(option
);
181 if (it
== stat_options
->end()) {
188 template <typename I
>
189 int get_pool_stats(librados::IoCtx
& io_ctx
, const ConfigProxy
& config
,
190 const std::vector
<std::string
>& image_ids
, uint64_t* image_count
,
191 uint64_t* provisioned_bytes
, uint64_t* max_provisioned_bytes
,
192 uint64_t* snapshot_count
) {
194 bool scan_snaps
= ((max_provisioned_bytes
!= nullptr) ||
195 (snapshot_count
!= nullptr));
197 SimpleThrottle
throttle(
198 config
.template get_val
<uint64_t>("rbd_concurrent_management_ops"), true);
199 std::atomic
<uint64_t> bytes
{0};
200 std::atomic
<uint64_t> max_bytes
{0};
201 std::atomic
<uint64_t> snaps
{0};
202 for (auto& image_id
: image_ids
) {
203 if (throttle
.pending_error()) {
207 auto req
= new ImageStatRequest
<I
>(io_ctx
, throttle
, image_id
,
208 scan_snaps
, &bytes
, &max_bytes
, &snaps
);
212 int r
= throttle
.wait_for_ret();
217 if (image_count
!= nullptr) {
218 *image_count
= image_ids
.size();
220 if (provisioned_bytes
!= nullptr) {
221 *provisioned_bytes
= bytes
.load();
223 if (max_provisioned_bytes
!= nullptr) {
224 *max_provisioned_bytes
= max_bytes
.load();
226 if (snapshot_count
!= nullptr) {
227 *snapshot_count
= snaps
.load();
233 } // anonymous namespace
236 #define dout_prefix *_dout << "librbd::api::Pool: " << __func__ << ": "
238 template <typename I
>
239 int Pool
<I
>::init(librados::IoCtx
& io_ctx
, bool force
) {
240 auto cct
= reinterpret_cast<CephContext
*>(io_ctx
.cct());
241 ldout(cct
, 10) << dendl
;
243 int r
= io_ctx
.application_enable(pg_pool_t::APPLICATION_NAME_RBD
, force
);
248 ConfigProxy config
{cct
->_conf
};
249 api::Config
<I
>::apply_pool_overrides(io_ctx
, &config
);
250 if (!config
.get_val
<bool>("rbd_validate_pool")) {
254 ThreadPool
*thread_pool
;
255 ContextWQ
*op_work_queue
;
256 ImageCtx::get_thread_pool_instance(cct
, &thread_pool
, &op_work_queue
);
259 auto req
= image::ValidatePoolRequest
<I
>::create(io_ctx
, op_work_queue
, &ctx
);
265 template <typename I
>
266 int Pool
<I
>::add_stat_option(StatOptions
* stat_options
,
267 rbd_pool_stat_option_t option
,
270 case RBD_POOL_STAT_OPTION_IMAGES
:
271 case RBD_POOL_STAT_OPTION_IMAGE_PROVISIONED_BYTES
:
272 case RBD_POOL_STAT_OPTION_IMAGE_MAX_PROVISIONED_BYTES
:
273 case RBD_POOL_STAT_OPTION_IMAGE_SNAPSHOTS
:
274 case RBD_POOL_STAT_OPTION_TRASH_IMAGES
:
275 case RBD_POOL_STAT_OPTION_TRASH_PROVISIONED_BYTES
:
276 case RBD_POOL_STAT_OPTION_TRASH_MAX_PROVISIONED_BYTES
:
277 case RBD_POOL_STAT_OPTION_TRASH_SNAPSHOTS
:
278 stat_options
->emplace(option
, value
);
286 template <typename I
>
287 int Pool
<I
>::get_stats(librados::IoCtx
& io_ctx
, StatOptions
* stat_options
) {
288 auto cct
= reinterpret_cast<CephContext
*>(io_ctx
.cct());
289 ldout(cct
, 10) << dendl
;
291 ConfigProxy config
{cct
->_conf
};
292 api::Config
<I
>::apply_pool_overrides(io_ctx
, &config
);
294 uint64_t* image_count
;
295 uint64_t* provisioned_bytes
;
296 uint64_t* max_provisioned_bytes
;
297 uint64_t* snapshot_count
;
299 std::vector
<trash_image_info_t
> trash_entries
;
300 int r
= Trash
<I
>::list(io_ctx
, trash_entries
, false);
301 if (r
< 0 && r
!= -EOPNOTSUPP
) {
305 get_pool_stat_option_value
<I
>(
306 stat_options
, RBD_POOL_STAT_OPTION_IMAGES
, &image_count
);
307 get_pool_stat_option_value
<I
>(
308 stat_options
, RBD_POOL_STAT_OPTION_IMAGE_PROVISIONED_BYTES
,
310 get_pool_stat_option_value
<I
>(
311 stat_options
, RBD_POOL_STAT_OPTION_IMAGE_MAX_PROVISIONED_BYTES
,
312 &max_provisioned_bytes
);
313 get_pool_stat_option_value
<I
>(
314 stat_options
, RBD_POOL_STAT_OPTION_IMAGE_SNAPSHOTS
, &snapshot_count
);
315 if (image_count
!= nullptr || provisioned_bytes
!= nullptr ||
316 max_provisioned_bytes
!= nullptr || snapshot_count
!= nullptr) {
317 typename Image
<I
>::ImageNameToIds images
;
318 int r
= Image
<I
>::list_images_v2(io_ctx
, &images
);
323 std::vector
<std::string
> image_ids
;
324 image_ids
.reserve(images
.size() + trash_entries
.size());
325 for (auto& it
: images
) {
326 image_ids
.push_back(std::move(it
.second
));
328 for (auto& it
: trash_entries
) {
329 if (it
.source
== RBD_TRASH_IMAGE_SOURCE_REMOVING
) {
330 image_ids
.push_back(std::move(it
.id
));
334 r
= get_pool_stats
<I
>(io_ctx
, config
, image_ids
, image_count
,
335 provisioned_bytes
, max_provisioned_bytes
,
342 get_pool_stat_option_value
<I
>(
343 stat_options
, RBD_POOL_STAT_OPTION_TRASH_IMAGES
, &image_count
);
344 get_pool_stat_option_value
<I
>(
345 stat_options
, RBD_POOL_STAT_OPTION_TRASH_PROVISIONED_BYTES
,
347 get_pool_stat_option_value
<I
>(
348 stat_options
, RBD_POOL_STAT_OPTION_TRASH_MAX_PROVISIONED_BYTES
,
349 &max_provisioned_bytes
);
350 get_pool_stat_option_value
<I
>(
351 stat_options
, RBD_POOL_STAT_OPTION_TRASH_SNAPSHOTS
, &snapshot_count
);
352 if (image_count
!= nullptr || provisioned_bytes
!= nullptr ||
353 max_provisioned_bytes
!= nullptr || snapshot_count
!= nullptr) {
355 std::vector
<std::string
> image_ids
;
356 image_ids
.reserve(trash_entries
.size());
357 for (auto& it
: trash_entries
) {
358 if (it
.source
== RBD_TRASH_IMAGE_SOURCE_REMOVING
) {
361 image_ids
.push_back(std::move(it
.id
));
364 r
= get_pool_stats
<I
>(io_ctx
, config
, image_ids
, image_count
,
365 provisioned_bytes
, max_provisioned_bytes
,
376 } // namespace librbd
378 template class librbd::api::Pool
<librbd::ImageCtx
>;