]> git.proxmox.com Git - ceph.git/blob - ceph/src/librbd/api/Pool.cc
import 15.2.0 Octopus source
[ceph.git] / ceph / src / librbd / api / Pool.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3
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"
18
19 #define dout_subsys ceph_subsys_rbd
20
21 namespace librbd {
22 namespace api {
23
24 namespace {
25
26 #undef dout_prefix
27 #define dout_prefix *_dout << "librbd::api::Pool::ImageStatRequest: " \
28 << __func__ << " " << this << ": " \
29 << "(id=" << m_image_id << "): "
30
31 template <typename I>
32 class ImageStatRequest {
33 public:
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),
42 m_snaps(snaps) {
43 m_throttle.start_op();
44 }
45
46 void send() {
47 get_head();
48 }
49
50 protected:
51 void finish(int r) {
52 (*m_max_bytes) += m_max_size;
53 m_throttle.end_op(r);
54
55 delete this;
56 }
57
58 private:
59 CephContext* m_cct;
60 librados::IoCtx& m_io_ctx;
61 SimpleThrottle& m_throttle;
62 const std::string& m_image_id;
63 bool m_scan_snaps;
64 std::atomic<uint64_t>* m_bytes;
65 std::atomic<uint64_t>* m_max_bytes;
66 std::atomic<uint64_t>* m_snaps;
67 bufferlist m_out_bl;
68
69 uint64_t m_max_size = 0;
70 ::SnapContext m_snapc;
71
72 void get_head() {
73 ldout(m_cct, 15) << dendl;
74
75 librados::ObjectReadOperation op;
76 cls_client::get_size_start(&op, CEPH_NOSNAP);
77 if (m_scan_snaps) {
78 cls_client::get_snapcontext_start(&op);
79 }
80
81 m_out_bl.clear();
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,
85 &m_out_bl);
86 ceph_assert(r == 0);
87 aio_comp->release();
88 }
89
90 void handle_get_head(int r) {
91 ldout(m_cct, 15) << "r=" << r << dendl;
92
93 auto it = m_out_bl.cbegin();
94 if (r == 0) {
95 uint8_t order;
96 r = cls_client::get_size_finish(&it, &m_max_size, &order);
97 if (r == 0) {
98 (*m_bytes) += m_max_size;
99 }
100 }
101 if (m_scan_snaps && r == 0) {
102 r = cls_client::get_snapcontext_finish(&it, &m_snapc);
103 if (r == 0) {
104 (*m_snaps) += m_snapc.snaps.size();
105 }
106 }
107
108 if (r == -ENOENT) {
109 finish(r);
110 return;
111 } else if (r < 0) {
112 lderr(m_cct) << "failed to stat image: " << cpp_strerror(r) << dendl;
113 finish(r);
114 return;
115 }
116
117 if (!m_snapc.is_valid()) {
118 lderr(m_cct) << "snap context is invalid" << dendl;
119 finish(-EIO);
120 return;
121 }
122
123 get_snaps();
124 }
125
126 void get_snaps() {
127 if (!m_scan_snaps || m_snapc.snaps.empty()) {
128 finish(0);
129 return;
130 }
131
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);
136 }
137
138 m_out_bl.clear();
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,
142 &m_out_bl);
143 ceph_assert(r == 0);
144 aio_comp->release();
145 }
146
147 void handle_get_snaps(int r) {
148 ldout(m_cct, 15) << "r=" << r << dendl;
149
150 auto it = m_out_bl.cbegin();
151 for ([[maybe_unused]] auto snap_seq : m_snapc.snaps) {
152 uint64_t size;
153 if (r == 0) {
154 uint8_t order;
155 r = cls_client::get_size_finish(&it, &size, &order);
156 }
157 if (r == 0 && m_max_size < size) {
158 m_max_size = size;
159 }
160 }
161
162 if (r == -ENOENT) {
163 ldout(m_cct, 15) << "out-of-sync metadata" << dendl;
164 get_head();
165 } else if (r < 0) {
166 lderr(m_cct) << "failed to retrieve snap size: " << cpp_strerror(r)
167 << dendl;
168 finish(r);
169 } else {
170 finish(0);
171 }
172 }
173
174 };
175
176 template <typename I>
177 void get_pool_stat_option_value(typename Pool<I>::StatOptions* stat_options,
178 rbd_pool_stat_option_t option,
179 uint64_t** value) {
180 auto it = stat_options->find(option);
181 if (it == stat_options->end()) {
182 *value = nullptr;
183 } else {
184 *value = it->second;
185 }
186 }
187
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) {
193
194 bool scan_snaps = ((max_provisioned_bytes != nullptr) ||
195 (snapshot_count != nullptr));
196
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()) {
204 break;
205 }
206
207 auto req = new ImageStatRequest<I>(io_ctx, throttle, image_id,
208 scan_snaps, &bytes, &max_bytes, &snaps);
209 req->send();
210 }
211
212 int r = throttle.wait_for_ret();
213 if (r < 0) {
214 return r;
215 }
216
217 if (image_count != nullptr) {
218 *image_count = image_ids.size();
219 }
220 if (provisioned_bytes != nullptr) {
221 *provisioned_bytes = bytes.load();
222 }
223 if (max_provisioned_bytes != nullptr) {
224 *max_provisioned_bytes = max_bytes.load();
225 }
226 if (snapshot_count != nullptr) {
227 *snapshot_count = snaps.load();
228 }
229
230 return 0;
231 }
232
233 } // anonymous namespace
234
235 #undef dout_prefix
236 #define dout_prefix *_dout << "librbd::api::Pool: " << __func__ << ": "
237
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;
242
243 int r = io_ctx.application_enable(pg_pool_t::APPLICATION_NAME_RBD, force);
244 if (r < 0) {
245 return r;
246 }
247
248 ConfigProxy config{cct->_conf};
249 api::Config<I>::apply_pool_overrides(io_ctx, &config);
250 if (!config.get_val<bool>("rbd_validate_pool")) {
251 return 0;
252 }
253
254 ThreadPool *thread_pool;
255 ContextWQ *op_work_queue;
256 ImageCtx::get_thread_pool_instance(cct, &thread_pool, &op_work_queue);
257
258 C_SaferCond ctx;
259 auto req = image::ValidatePoolRequest<I>::create(io_ctx, op_work_queue, &ctx);
260 req->send();
261
262 return ctx.wait();
263 }
264
265 template <typename I>
266 int Pool<I>::add_stat_option(StatOptions* stat_options,
267 rbd_pool_stat_option_t option,
268 uint64_t* value) {
269 switch (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);
279 return 0;
280 default:
281 break;
282 }
283 return -ENOENT;
284 }
285
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;
290
291 ConfigProxy config{cct->_conf};
292 api::Config<I>::apply_pool_overrides(io_ctx, &config);
293
294 uint64_t* image_count;
295 uint64_t* provisioned_bytes;
296 uint64_t* max_provisioned_bytes;
297 uint64_t* snapshot_count;
298
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) {
302 return r;
303 }
304
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,
309 &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);
319 if (r < 0) {
320 return r;
321 }
322
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));
327 }
328 for (auto& it : trash_entries) {
329 if (it.source == RBD_TRASH_IMAGE_SOURCE_REMOVING) {
330 image_ids.push_back(std::move(it.id));
331 }
332 }
333
334 r = get_pool_stats<I>(io_ctx, config, image_ids, image_count,
335 provisioned_bytes, max_provisioned_bytes,
336 snapshot_count);
337 if (r < 0) {
338 return r;
339 }
340 }
341
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,
346 &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) {
354
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) {
359 continue;
360 }
361 image_ids.push_back(std::move(it.id));
362 }
363
364 r = get_pool_stats<I>(io_ctx, config, image_ids, image_count,
365 provisioned_bytes, max_provisioned_bytes,
366 snapshot_count);
367 if (r < 0) {
368 return r;
369 }
370 }
371
372 return 0;
373 }
374
375 } // namespace api
376 } // namespace librbd
377
378 template class librbd::api::Pool<librbd::ImageCtx>;