]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | ||
4 | #include "include/compat.h" | |
5 | #include "tools/rbd/ArgumentTypes.h" | |
6 | #include "tools/rbd/Shell.h" | |
7 | #include "tools/rbd/Utils.h" | |
8 | #include "include/Context.h" | |
9 | #include "common/errno.h" | |
10 | #include "common/Throttle.h" | |
11 | #include "include/encoding.h" | |
12 | #include <iostream> | |
13 | #include <fcntl.h> | |
14 | #include <stdlib.h> | |
15 | #include <boost/program_options.hpp> | |
16 | #include <boost/scope_exit.hpp> | |
17 | ||
20effc67 TL |
18 | using std::cerr; |
19 | using std::string; | |
20 | ||
7c673cae FG |
21 | namespace rbd { |
22 | namespace action { | |
23 | namespace export_full { | |
24 | ||
25 | struct ExportDiffContext { | |
26 | librbd::Image *image; | |
27 | int fd; | |
28 | int export_format; | |
29 | uint64_t totalsize; | |
30 | utils::ProgressContext pc; | |
31 | OrderedThrottle throttle; | |
32 | ||
33 | ExportDiffContext(librbd::Image *i, int f, uint64_t t, int max_ops, | |
34 | bool no_progress, int eformat) : | |
35 | image(i), fd(f), export_format(eformat), totalsize(t), pc("Exporting image", no_progress), | |
36 | throttle(max_ops, true) { | |
37 | } | |
38 | }; | |
39 | ||
40 | class C_ExportDiff : public Context { | |
41 | public: | |
42 | C_ExportDiff(ExportDiffContext *edc, uint64_t offset, uint64_t length, | |
43 | bool exists, int export_format) | |
44 | : m_export_diff_context(edc), m_offset(offset), m_length(length), | |
45 | m_exists(exists), m_export_format(export_format) { | |
46 | } | |
47 | ||
48 | int send() { | |
49 | if (m_export_diff_context->throttle.pending_error()) { | |
50 | return m_export_diff_context->throttle.wait_for_ret(); | |
51 | } | |
52 | ||
53 | C_OrderedThrottle *ctx = m_export_diff_context->throttle.start_op(this); | |
54 | if (m_exists) { | |
55 | librbd::RBD::AioCompletion *aio_completion = | |
56 | new librbd::RBD::AioCompletion(ctx, &utils::aio_context_callback); | |
57 | ||
58 | int op_flags = LIBRADOS_OP_FLAG_FADVISE_NOCACHE; | |
59 | int r = m_export_diff_context->image->aio_read2( | |
60 | m_offset, m_length, m_read_data, aio_completion, op_flags); | |
61 | if (r < 0) { | |
62 | aio_completion->release(); | |
63 | ctx->complete(r); | |
64 | } | |
65 | } else { | |
66 | ctx->complete(0); | |
67 | } | |
68 | return 0; | |
69 | } | |
70 | ||
71 | static int export_diff_cb(uint64_t offset, size_t length, int exists, | |
72 | void *arg) { | |
73 | ExportDiffContext *edc = reinterpret_cast<ExportDiffContext *>(arg); | |
74 | ||
75 | C_ExportDiff *context = new C_ExportDiff(edc, offset, length, exists, edc->export_format); | |
76 | return context->send(); | |
77 | } | |
78 | ||
79 | protected: | |
80 | void finish(int r) override { | |
81 | if (r >= 0) { | |
82 | if (m_exists) { | |
83 | m_exists = !m_read_data.is_zero(); | |
84 | } | |
85 | r = write_extent(m_export_diff_context, m_offset, m_length, m_exists, m_export_format); | |
86 | if (r == 0 && m_exists) { | |
87 | r = m_read_data.write_fd(m_export_diff_context->fd); | |
88 | } | |
89 | } | |
90 | m_export_diff_context->throttle.end_op(r); | |
91 | } | |
92 | ||
93 | private: | |
94 | ExportDiffContext *m_export_diff_context; | |
95 | uint64_t m_offset; | |
96 | uint64_t m_length; | |
97 | bool m_exists; | |
98 | int m_export_format; | |
99 | bufferlist m_read_data; | |
100 | ||
101 | static int write_extent(ExportDiffContext *edc, uint64_t offset, | |
102 | uint64_t length, bool exists, int export_format) { | |
103 | // extent | |
104 | bufferlist bl; | |
105 | __u8 tag = exists ? RBD_DIFF_WRITE : RBD_DIFF_ZERO; | |
106 | uint64_t len = 0; | |
11fdf7f2 | 107 | encode(tag, bl); |
7c673cae FG |
108 | if (export_format == 2) { |
109 | if (tag == RBD_DIFF_WRITE) | |
110 | len = 8 + 8 + length; | |
111 | else | |
112 | len = 8 + 8; | |
11fdf7f2 | 113 | encode(len, bl); |
7c673cae | 114 | } |
11fdf7f2 TL |
115 | encode(offset, bl); |
116 | encode(length, bl); | |
7c673cae FG |
117 | int r = bl.write_fd(edc->fd); |
118 | ||
119 | edc->pc.update_progress(offset, edc->totalsize); | |
120 | return r; | |
121 | } | |
122 | }; | |
123 | ||
124 | ||
125 | int do_export_diff_fd(librbd::Image& image, const char *fromsnapname, | |
126 | const char *endsnapname, bool whole_object, | |
127 | int fd, bool no_progress, int export_format) | |
128 | { | |
129 | int r; | |
130 | librbd::image_info_t info; | |
131 | ||
132 | r = image.stat(info, sizeof(info)); | |
133 | if (r < 0) | |
134 | return r; | |
135 | ||
136 | { | |
137 | // header | |
138 | bufferlist bl; | |
139 | if (export_format == 1) | |
140 | bl.append(utils::RBD_DIFF_BANNER); | |
141 | else | |
142 | bl.append(utils::RBD_DIFF_BANNER_V2); | |
143 | ||
144 | __u8 tag; | |
145 | uint64_t len = 0; | |
146 | if (fromsnapname) { | |
147 | tag = RBD_DIFF_FROM_SNAP; | |
11fdf7f2 | 148 | encode(tag, bl); |
7c673cae FG |
149 | std::string from(fromsnapname); |
150 | if (export_format == 2) { | |
151 | len = from.length() + 4; | |
11fdf7f2 | 152 | encode(len, bl); |
7c673cae | 153 | } |
11fdf7f2 | 154 | encode(from, bl); |
7c673cae FG |
155 | } |
156 | ||
157 | if (endsnapname) { | |
158 | tag = RBD_DIFF_TO_SNAP; | |
11fdf7f2 | 159 | encode(tag, bl); |
7c673cae FG |
160 | std::string to(endsnapname); |
161 | if (export_format == 2) { | |
28e407b8 | 162 | len = to.length() + 4; |
11fdf7f2 | 163 | encode(len, bl); |
7c673cae | 164 | } |
11fdf7f2 | 165 | encode(to, bl); |
7c673cae FG |
166 | } |
167 | ||
28e407b8 AA |
168 | if (endsnapname && export_format == 2) { |
169 | tag = RBD_SNAP_PROTECTION_STATUS; | |
170 | encode(tag, bl); | |
171 | bool is_protected = false; | |
172 | r = image.snap_is_protected(endsnapname, &is_protected); | |
173 | if (r < 0) { | |
174 | return r; | |
175 | } | |
20effc67 | 176 | len = 1; |
28e407b8 AA |
177 | encode(len, bl); |
178 | encode(is_protected, bl); | |
179 | } | |
180 | ||
7c673cae | 181 | tag = RBD_DIFF_IMAGE_SIZE; |
11fdf7f2 | 182 | encode(tag, bl); |
7c673cae FG |
183 | uint64_t endsize = info.size; |
184 | if (export_format == 2) { | |
185 | len = 8; | |
11fdf7f2 | 186 | encode(len, bl); |
7c673cae | 187 | } |
11fdf7f2 | 188 | encode(endsize, bl); |
7c673cae FG |
189 | |
190 | r = bl.write_fd(fd); | |
191 | if (r < 0) { | |
192 | return r; | |
193 | } | |
194 | } | |
195 | ExportDiffContext edc(&image, fd, info.size, | |
11fdf7f2 | 196 | g_conf().get_val<uint64_t>("rbd_concurrent_management_ops"), |
181888fb | 197 | no_progress, export_format); |
7c673cae FG |
198 | r = image.diff_iterate2(fromsnapname, 0, info.size, true, whole_object, |
199 | &C_ExportDiff::export_diff_cb, (void *)&edc); | |
200 | if (r < 0) { | |
201 | goto out; | |
202 | } | |
203 | ||
204 | r = edc.throttle.wait_for_ret(); | |
205 | if (r < 0) { | |
206 | goto out; | |
207 | } | |
208 | ||
209 | { | |
210 | __u8 tag = RBD_DIFF_END; | |
211 | bufferlist bl; | |
11fdf7f2 | 212 | encode(tag, bl); |
7c673cae FG |
213 | r = bl.write_fd(fd); |
214 | } | |
215 | ||
216 | out: | |
217 | if (r < 0) | |
218 | edc.pc.fail(); | |
219 | else | |
220 | edc.pc.finish(); | |
221 | ||
222 | return r; | |
223 | } | |
224 | ||
225 | int do_export_diff(librbd::Image& image, const char *fromsnapname, | |
226 | const char *endsnapname, bool whole_object, | |
227 | const char *path, bool no_progress) | |
228 | { | |
229 | int r; | |
230 | int fd; | |
231 | ||
232 | if (strcmp(path, "-") == 0) | |
233 | fd = STDOUT_FILENO; | |
234 | else | |
f67539c2 | 235 | fd = open(path, O_WRONLY | O_CREAT | O_EXCL | O_BINARY, 0644); |
7c673cae FG |
236 | if (fd < 0) |
237 | return -errno; | |
238 | ||
239 | r = do_export_diff_fd(image, fromsnapname, endsnapname, whole_object, fd, no_progress, 1); | |
240 | ||
241 | if (fd != 1) | |
242 | close(fd); | |
243 | if (r < 0 && fd != 1) { | |
244 | remove(path); | |
245 | } | |
246 | ||
247 | return r; | |
248 | } | |
249 | ||
250 | ||
251 | namespace at = argument_types; | |
252 | namespace po = boost::program_options; | |
253 | ||
254 | void get_arguments_diff(po::options_description *positional, | |
255 | po::options_description *options) { | |
256 | at::add_image_or_snap_spec_options(positional, options, | |
257 | at::ARGUMENT_MODIFIER_SOURCE); | |
258 | at::add_path_options(positional, options, | |
259 | "export file (or '-' for stdout)"); | |
260 | options->add_options() | |
261 | (at::FROM_SNAPSHOT_NAME.c_str(), po::value<std::string>(), | |
262 | "snapshot starting point") | |
263 | (at::WHOLE_OBJECT.c_str(), po::bool_switch(), "compare whole object"); | |
264 | at::add_no_progress_option(options); | |
265 | } | |
266 | ||
11fdf7f2 TL |
267 | int execute_diff(const po::variables_map &vm, |
268 | const std::vector<std::string> &ceph_global_init_args) { | |
7c673cae FG |
269 | size_t arg_index = 0; |
270 | std::string pool_name; | |
11fdf7f2 | 271 | std::string namespace_name; |
7c673cae FG |
272 | std::string image_name; |
273 | std::string snap_name; | |
274 | int r = utils::get_pool_image_snapshot_names( | |
11fdf7f2 TL |
275 | vm, at::ARGUMENT_MODIFIER_SOURCE, &arg_index, &pool_name, &namespace_name, |
276 | &image_name, &snap_name, true, utils::SNAPSHOT_PRESENCE_PERMITTED, | |
7c673cae FG |
277 | utils::SPEC_VALIDATION_NONE); |
278 | if (r < 0) { | |
279 | return r; | |
280 | } | |
281 | ||
282 | std::string path; | |
11fdf7f2 | 283 | r = utils::get_path(vm, &arg_index, &path); |
7c673cae FG |
284 | if (r < 0) { |
285 | return r; | |
286 | } | |
287 | ||
288 | std::string from_snap_name; | |
289 | if (vm.count(at::FROM_SNAPSHOT_NAME)) { | |
290 | from_snap_name = vm[at::FROM_SNAPSHOT_NAME].as<std::string>(); | |
291 | } | |
292 | ||
293 | librados::Rados rados; | |
294 | librados::IoCtx io_ctx; | |
295 | librbd::Image image; | |
11fdf7f2 TL |
296 | r = utils::init_and_open_image(pool_name, namespace_name, image_name, "", |
297 | snap_name, true, &rados, &io_ctx, &image); | |
7c673cae FG |
298 | if (r < 0) { |
299 | return r; | |
300 | } | |
301 | ||
302 | r = do_export_diff(image, | |
303 | from_snap_name.empty() ? nullptr : from_snap_name.c_str(), | |
304 | snap_name.empty() ? nullptr : snap_name.c_str(), | |
305 | vm[at::WHOLE_OBJECT].as<bool>(), path.c_str(), | |
306 | vm[at::NO_PROGRESS].as<bool>()); | |
307 | if (r < 0) { | |
308 | std::cerr << "rbd: export-diff error: " << cpp_strerror(r) << std::endl; | |
309 | return r; | |
310 | } | |
311 | return 0; | |
312 | } | |
313 | ||
7c673cae FG |
314 | Shell::Action action_diff( |
315 | {"export-diff"}, {}, "Export incremental diff to file.", "", | |
316 | &get_arguments_diff, &execute_diff); | |
317 | ||
318 | class C_Export : public Context | |
319 | { | |
320 | public: | |
494da23a | 321 | C_Export(OrderedThrottle &ordered_throttle, librbd::Image &image, |
7c673cae | 322 | uint64_t fd_offset, uint64_t offset, uint64_t length, int fd) |
494da23a | 323 | : m_throttle(ordered_throttle), m_image(image), m_dest_offset(fd_offset), |
7c673cae FG |
324 | m_offset(offset), m_length(length), m_fd(fd) |
325 | { | |
326 | } | |
327 | ||
328 | void send() | |
329 | { | |
494da23a TL |
330 | auto ctx = m_throttle.start_op(this); |
331 | auto aio_completion = new librbd::RBD::AioCompletion( | |
332 | ctx, &utils::aio_context_callback); | |
7c673cae FG |
333 | int op_flags = LIBRADOS_OP_FLAG_FADVISE_SEQUENTIAL | |
334 | LIBRADOS_OP_FLAG_FADVISE_NOCACHE; | |
335 | int r = m_image.aio_read2(m_offset, m_length, m_bufferlist, | |
494da23a | 336 | aio_completion, op_flags); |
7c673cae FG |
337 | if (r < 0) { |
338 | cerr << "rbd: error requesting read from source image" << std::endl; | |
494da23a | 339 | aio_completion->release(); |
7c673cae FG |
340 | m_throttle.end_op(r); |
341 | } | |
342 | } | |
343 | ||
344 | void finish(int r) override | |
345 | { | |
346 | BOOST_SCOPE_EXIT((&m_throttle) (&r)) | |
347 | { | |
348 | m_throttle.end_op(r); | |
349 | } BOOST_SCOPE_EXIT_END | |
350 | ||
351 | if (r < 0) { | |
352 | cerr << "rbd: error reading from source image at offset " | |
353 | << m_offset << ": " << cpp_strerror(r) << std::endl; | |
354 | return; | |
355 | } | |
356 | ||
11fdf7f2 | 357 | ceph_assert(m_bufferlist.length() == static_cast<size_t>(r)); |
7c673cae FG |
358 | if (m_fd != STDOUT_FILENO) { |
359 | if (m_bufferlist.is_zero()) { | |
360 | return; | |
361 | } | |
362 | ||
363 | uint64_t chkret = lseek64(m_fd, m_dest_offset, SEEK_SET); | |
364 | if (chkret != m_dest_offset) { | |
365 | cerr << "rbd: error seeking destination image to offset " | |
366 | << m_dest_offset << std::endl; | |
367 | r = -errno; | |
368 | return; | |
369 | } | |
370 | } | |
371 | ||
372 | r = m_bufferlist.write_fd(m_fd); | |
373 | if (r < 0) { | |
374 | cerr << "rbd: error writing to destination image at offset " | |
375 | << m_dest_offset << std::endl; | |
376 | } | |
377 | } | |
378 | ||
379 | private: | |
494da23a | 380 | OrderedThrottle &m_throttle; |
7c673cae FG |
381 | librbd::Image &m_image; |
382 | bufferlist m_bufferlist; | |
383 | uint64_t m_dest_offset; | |
384 | uint64_t m_offset; | |
385 | uint64_t m_length; | |
386 | int m_fd; | |
387 | }; | |
388 | ||
28e407b8 AA |
389 | const uint32_t MAX_KEYS = 64; |
390 | ||
7c673cae FG |
391 | static int do_export_v2(librbd::Image& image, librbd::image_info_t &info, int fd, |
392 | uint64_t period, int max_concurrent_ops, utils::ProgressContext &pc) | |
393 | { | |
394 | int r = 0; | |
395 | // header | |
396 | bufferlist bl; | |
397 | bl.append(utils::RBD_IMAGE_BANNER_V2); | |
398 | ||
399 | __u8 tag; | |
400 | uint64_t length; | |
401 | // encode order | |
402 | tag = RBD_EXPORT_IMAGE_ORDER; | |
403 | length = 8; | |
11fdf7f2 TL |
404 | encode(tag, bl); |
405 | encode(length, bl); | |
406 | encode(uint64_t(info.order), bl); | |
7c673cae FG |
407 | |
408 | // encode features | |
409 | tag = RBD_EXPORT_IMAGE_FEATURES; | |
410 | uint64_t features; | |
411 | image.features(&features); | |
412 | length = 8; | |
11fdf7f2 TL |
413 | encode(tag, bl); |
414 | encode(length, bl); | |
415 | encode(features, bl); | |
7c673cae FG |
416 | |
417 | // encode stripe_unit and stripe_count | |
418 | tag = RBD_EXPORT_IMAGE_STRIPE_UNIT; | |
419 | uint64_t stripe_unit; | |
420 | stripe_unit = image.get_stripe_unit(); | |
421 | length = 8; | |
11fdf7f2 TL |
422 | encode(tag, bl); |
423 | encode(length, bl); | |
424 | encode(stripe_unit, bl); | |
7c673cae FG |
425 | |
426 | tag = RBD_EXPORT_IMAGE_STRIPE_COUNT; | |
427 | uint64_t stripe_count; | |
428 | stripe_count = image.get_stripe_count(); | |
429 | length = 8; | |
11fdf7f2 TL |
430 | encode(tag, bl); |
431 | encode(length, bl); | |
432 | encode(stripe_count, bl); | |
7c673cae | 433 | |
28e407b8 AA |
434 | //retrieve metadata of image |
435 | std::map<std::string, string> imagemetas; | |
436 | std::string last_key; | |
437 | bool more_results = true; | |
438 | while (more_results) { | |
439 | std::map<std::string, bufferlist> pairs; | |
440 | r = image.metadata_list(last_key, MAX_KEYS, &pairs); | |
441 | if (r < 0) { | |
442 | std::cerr << "failed to retrieve metadata of image : " << cpp_strerror(r) | |
443 | << std::endl; | |
444 | return r; | |
445 | } | |
446 | ||
447 | if (!pairs.empty()) { | |
448 | last_key = pairs.rbegin()->first; | |
449 | ||
450 | for (auto kv : pairs) { | |
451 | std::string key = kv.first; | |
452 | std::string val(kv.second.c_str(), kv.second.length()); | |
453 | imagemetas[key] = val; | |
454 | } | |
455 | } | |
456 | more_results = (pairs.size() == MAX_KEYS); | |
457 | } | |
458 | ||
459 | //encode imageMeta key and value | |
460 | for (std::map<std::string, string>::iterator it = imagemetas.begin(); | |
461 | it != imagemetas.end(); ++it) { | |
462 | string key = it->first; | |
463 | string value = it->second; | |
464 | ||
465 | tag = RBD_EXPORT_IMAGE_META; | |
466 | length = key.length() + value.length() + 4 * 2; | |
11fdf7f2 TL |
467 | encode(tag, bl); |
468 | encode(length, bl); | |
469 | encode(key, bl); | |
470 | encode(value, bl); | |
28e407b8 AA |
471 | } |
472 | ||
7c673cae FG |
473 | // encode end tag |
474 | tag = RBD_EXPORT_IMAGE_END; | |
11fdf7f2 | 475 | encode(tag, bl); |
7c673cae FG |
476 | |
477 | // write bl to fd. | |
478 | r = bl.write_fd(fd); | |
479 | if (r < 0) { | |
480 | return r; | |
481 | } | |
482 | ||
483 | // header for snapshots | |
484 | bl.clear(); | |
485 | bl.append(utils::RBD_IMAGE_DIFFS_BANNER_V2); | |
486 | ||
487 | std::vector<librbd::snap_info_t> snaps; | |
488 | r = image.snap_list(snaps); | |
489 | if (r < 0) { | |
490 | return r; | |
491 | } | |
492 | ||
493 | uint64_t diff_num = snaps.size() + 1; | |
11fdf7f2 | 494 | encode(diff_num, bl); |
7c673cae FG |
495 | |
496 | r = bl.write_fd(fd); | |
497 | if (r < 0) { | |
498 | return r; | |
499 | } | |
500 | ||
501 | const char *last_snap = NULL; | |
502 | for (size_t i = 0; i < snaps.size(); ++i) { | |
503 | utils::snap_set(image, snaps[i].name.c_str()); | |
504 | r = do_export_diff_fd(image, last_snap, snaps[i].name.c_str(), false, fd, true, 2); | |
505 | if (r < 0) { | |
506 | return r; | |
507 | } | |
508 | pc.update_progress(i, snaps.size() + 1); | |
509 | last_snap = snaps[i].name.c_str(); | |
510 | } | |
511 | utils::snap_set(image, std::string("")); | |
512 | r = do_export_diff_fd(image, last_snap, nullptr, false, fd, true, 2); | |
513 | if (r < 0) { | |
514 | return r; | |
515 | } | |
516 | pc.update_progress(snaps.size() + 1, snaps.size() + 1); | |
517 | return r; | |
518 | } | |
519 | ||
494da23a TL |
520 | static int do_export_v1(librbd::Image& image, librbd::image_info_t &info, |
521 | int fd, uint64_t period, int max_concurrent_ops, | |
522 | utils::ProgressContext &pc) | |
7c673cae FG |
523 | { |
524 | int r = 0; | |
525 | size_t file_size = 0; | |
494da23a | 526 | OrderedThrottle throttle(max_concurrent_ops, false); |
7c673cae FG |
527 | for (uint64_t offset = 0; offset < info.size; offset += period) { |
528 | if (throttle.pending_error()) { | |
529 | break; | |
530 | } | |
531 | ||
20effc67 | 532 | uint64_t length = std::min(period, info.size - offset); |
494da23a TL |
533 | C_Export *ctx = new C_Export(throttle, image, file_size + offset, offset, |
534 | length, fd); | |
7c673cae FG |
535 | ctx->send(); |
536 | ||
537 | pc.update_progress(offset, info.size); | |
538 | } | |
539 | ||
540 | file_size += info.size; | |
541 | r = throttle.wait_for_ret(); | |
542 | if (fd != 1) { | |
543 | if (r >= 0) { | |
544 | r = ftruncate(fd, file_size); | |
545 | if (r < 0) | |
546 | return r; | |
547 | ||
548 | uint64_t chkret = lseek64(fd, file_size, SEEK_SET); | |
549 | if (chkret != file_size) | |
550 | r = errno; | |
551 | } | |
552 | } | |
553 | return r; | |
554 | } | |
555 | ||
494da23a TL |
556 | static int do_export(librbd::Image& image, const char *path, bool no_progress, |
557 | int export_format) | |
7c673cae FG |
558 | { |
559 | librbd::image_info_t info; | |
560 | int64_t r = image.stat(info, sizeof(info)); | |
561 | if (r < 0) | |
562 | return r; | |
563 | ||
564 | int fd; | |
494da23a | 565 | int max_concurrent_ops = g_conf().get_val<uint64_t>("rbd_concurrent_management_ops"); |
7c673cae FG |
566 | bool to_stdout = (strcmp(path, "-") == 0); |
567 | if (to_stdout) { | |
568 | fd = STDOUT_FILENO; | |
7c673cae | 569 | } else { |
f67539c2 | 570 | fd = open(path, O_WRONLY | O_CREAT | O_EXCL | O_BINARY, 0644); |
7c673cae FG |
571 | if (fd < 0) { |
572 | return -errno; | |
573 | } | |
574 | #ifdef HAVE_POSIX_FADVISE | |
575 | posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL); | |
576 | #endif | |
577 | } | |
578 | ||
579 | utils::ProgressContext pc("Exporting image", no_progress); | |
580 | uint64_t period = image.get_stripe_count() * (1ull << info.order); | |
581 | ||
582 | if (export_format == 1) | |
583 | r = do_export_v1(image, info, fd, period, max_concurrent_ops, pc); | |
584 | else | |
585 | r = do_export_v2(image, info, fd, period, max_concurrent_ops, pc); | |
586 | ||
587 | if (r < 0) | |
588 | pc.fail(); | |
589 | else | |
590 | pc.finish(); | |
591 | if (!to_stdout) | |
592 | close(fd); | |
593 | return r; | |
594 | } | |
595 | ||
596 | void get_arguments(po::options_description *positional, | |
597 | po::options_description *options) { | |
598 | at::add_image_or_snap_spec_options(positional, options, | |
599 | at::ARGUMENT_MODIFIER_SOURCE); | |
600 | at::add_path_options(positional, options, | |
601 | "export file (or '-' for stdout)"); | |
602 | at::add_no_progress_option(options); | |
603 | at::add_export_format_option(options); | |
604 | } | |
605 | ||
11fdf7f2 TL |
606 | int execute(const po::variables_map &vm, |
607 | const std::vector<std::string> &ceph_global_init_args) { | |
7c673cae FG |
608 | size_t arg_index = 0; |
609 | std::string pool_name; | |
11fdf7f2 | 610 | std::string namespace_name; |
7c673cae FG |
611 | std::string image_name; |
612 | std::string snap_name; | |
613 | int r = utils::get_pool_image_snapshot_names( | |
11fdf7f2 TL |
614 | vm, at::ARGUMENT_MODIFIER_SOURCE, &arg_index, &pool_name, &namespace_name, |
615 | &image_name, &snap_name, true, utils::SNAPSHOT_PRESENCE_PERMITTED, | |
7c673cae FG |
616 | utils::SPEC_VALIDATION_NONE); |
617 | if (r < 0) { | |
618 | return r; | |
619 | } | |
620 | ||
621 | std::string path; | |
11fdf7f2 | 622 | r = utils::get_path(vm, &arg_index, &path); |
7c673cae FG |
623 | if (r < 0) { |
624 | return r; | |
625 | } | |
626 | ||
627 | librados::Rados rados; | |
628 | librados::IoCtx io_ctx; | |
629 | librbd::Image image; | |
11fdf7f2 TL |
630 | r = utils::init_and_open_image(pool_name, namespace_name, image_name, "", |
631 | snap_name, true, &rados, &io_ctx, &image); | |
7c673cae FG |
632 | if (r < 0) { |
633 | return r; | |
634 | } | |
11fdf7f2 | 635 | |
7c673cae FG |
636 | int format = 1; |
637 | if (vm.count("export-format")) | |
638 | format = vm["export-format"].as<uint64_t>(); | |
639 | ||
640 | r = do_export(image, path.c_str(), vm[at::NO_PROGRESS].as<bool>(), format); | |
641 | if (r < 0) { | |
642 | std::cerr << "rbd: export error: " << cpp_strerror(r) << std::endl; | |
643 | return r; | |
644 | } | |
645 | return 0; | |
646 | } | |
647 | ||
648 | Shell::Action action( | |
649 | {"export"}, {}, "Export image to file.", "", &get_arguments, &execute); | |
650 | ||
651 | } // namespace export_full | |
652 | } // namespace action | |
653 | } // namespace rbd |