]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
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" | |
9f95a23c | 8 | #include "common/Cond.h" |
11fdf7f2 TL |
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>; |