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