]> git.proxmox.com Git - ceph.git/blame - ceph/src/tools/rbd/action/Export.cc
bump version to 18.2.2-pve1
[ceph.git] / ceph / src / tools / rbd / action / Export.cc
CommitLineData
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
18using std::cerr;
19using std::string;
20
7c673cae
FG
21namespace rbd {
22namespace action {
23namespace export_full {
24
25struct 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
40class C_ExportDiff : public Context {
41public:
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
79protected:
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
93private:
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
125int 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
216out:
217 if (r < 0)
218 edc.pc.fail();
219 else
220 edc.pc.finish();
221
222 return r;
223}
224
225int 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
251namespace at = argument_types;
252namespace po = boost::program_options;
253
254void 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
267int 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
314Shell::Action action_diff(
315 {"export-diff"}, {}, "Export incremental diff to file.", "",
316 &get_arguments_diff, &execute_diff);
317
318class C_Export : public Context
319{
320public:
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
379private:
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
389const uint32_t MAX_KEYS = 64;
390
7c673cae
FG
391static 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
520static 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
556static 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
596void 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
606int 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
648Shell::Action action(
649 {"export"}, {}, "Export image to file.", "", &get_arguments, &execute);
650
651} // namespace export_full
652} // namespace action
653} // namespace rbd