Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / tools / rbd / action / Export.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 "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
18 namespace rbd {
19 namespace action {
20 namespace export_full {
21
22 struct 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
37 class C_ExportDiff : public Context {
38 public:
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
76 protected:
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
90 private:
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;
104     ::encode(tag, bl);
105     if (export_format == 2) {
106       if (tag == RBD_DIFF_WRITE)
107         len = 8 + 8 + length;
108       else
109         len = 8 + 8;
110       ::encode(len, bl);
111     }
112     ::encode(offset, bl);
113     ::encode(length, bl);
114     int r = bl.write_fd(edc->fd);
115
116     edc->pc.update_progress(offset, edc->totalsize);
117     return r;
118   }
119 };
120
121
122 int 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;
145       ::encode(tag, bl);
146       std::string from(fromsnapname);
147       if (export_format == 2) {
148         len = from.length() + 4;
149         ::encode(len, bl);
150       }
151       ::encode(from, bl);
152     }
153
154     if (endsnapname) {
155       tag = RBD_DIFF_TO_SNAP;
156       ::encode(tag, bl);
157       std::string to(endsnapname);
158       if (export_format == 2) {
159         len = to.length() + 4;
160         ::encode(len, bl);
161       }
162       ::encode(to, bl);
163     }
164
165     tag = RBD_DIFF_IMAGE_SIZE;
166     ::encode(tag, bl);
167     uint64_t endsize = info.size;
168     if (export_format == 2) {
169       len = 8;
170       ::encode(len, bl);
171     }
172     ::encode(endsize, bl);
173
174     r = bl.write_fd(fd);
175     if (r < 0) {
176       return r;
177     }
178   }
179   ExportDiffContext edc(&image, fd, info.size,
180                         g_conf->get_val<int64_t>("rbd_concurrent_management_ops"),
181                         no_progress, export_format);
182   r = image.diff_iterate2(fromsnapname, 0, info.size, true, whole_object,
183                           &C_ExportDiff::export_diff_cb, (void *)&edc);
184   if (r < 0) {
185     goto out;
186   }
187
188   r = edc.throttle.wait_for_ret();
189   if (r < 0) {
190     goto out;
191   }
192
193   {
194     __u8 tag = RBD_DIFF_END;
195     bufferlist bl;
196     ::encode(tag, bl);
197     r = bl.write_fd(fd);
198   }
199
200 out:
201   if (r < 0)
202     edc.pc.fail();
203   else
204     edc.pc.finish();
205
206   return r;
207 }
208
209 int do_export_diff(librbd::Image& image, const char *fromsnapname,
210                 const char *endsnapname, bool whole_object,
211                 const char *path, bool no_progress)
212 {
213   int r;
214   int fd;
215
216   if (strcmp(path, "-") == 0)
217     fd = STDOUT_FILENO;
218   else
219     fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0644);
220   if (fd < 0)
221     return -errno;
222
223   r = do_export_diff_fd(image, fromsnapname, endsnapname, whole_object, fd, no_progress, 1);
224
225   if (fd != 1)
226     close(fd);
227   if (r < 0 && fd != 1) {
228     remove(path);
229   }
230
231   return r;
232 }
233
234
235 namespace at = argument_types;
236 namespace po = boost::program_options;
237
238 void get_arguments_diff(po::options_description *positional,
239                    po::options_description *options) {
240   at::add_image_or_snap_spec_options(positional, options,
241                                      at::ARGUMENT_MODIFIER_SOURCE);
242   at::add_path_options(positional, options,
243                        "export file (or '-' for stdout)");
244   options->add_options()
245     (at::FROM_SNAPSHOT_NAME.c_str(), po::value<std::string>(),
246      "snapshot starting point")
247     (at::WHOLE_OBJECT.c_str(), po::bool_switch(), "compare whole object");
248   at::add_no_progress_option(options);
249 }
250
251 int execute_diff(const po::variables_map &vm) {
252   size_t arg_index = 0;
253   std::string pool_name;
254   std::string image_name;
255   std::string snap_name;
256   int r = utils::get_pool_image_snapshot_names(
257     vm, at::ARGUMENT_MODIFIER_SOURCE, &arg_index, &pool_name, &image_name,
258     &snap_name, utils::SNAPSHOT_PRESENCE_PERMITTED,
259     utils::SPEC_VALIDATION_NONE);
260   if (r < 0) {
261     return r;
262   }
263
264   std::string path;
265   r = utils::get_path(vm, utils::get_positional_argument(vm, 1), &path);
266   if (r < 0) {
267     return r;
268   }
269
270   std::string from_snap_name;
271   if (vm.count(at::FROM_SNAPSHOT_NAME)) {
272     from_snap_name = vm[at::FROM_SNAPSHOT_NAME].as<std::string>();
273   }
274
275   librados::Rados rados;
276   librados::IoCtx io_ctx;
277   librbd::Image image;
278   r = utils::init_and_open_image(pool_name, image_name, "", snap_name, true,
279                                  &rados, &io_ctx, &image);
280   if (r < 0) {
281     return r;
282   }
283
284   r = do_export_diff(image,
285                      from_snap_name.empty() ? nullptr : from_snap_name.c_str(),
286                      snap_name.empty() ? nullptr : snap_name.c_str(),
287                      vm[at::WHOLE_OBJECT].as<bool>(), path.c_str(),
288                      vm[at::NO_PROGRESS].as<bool>());
289   if (r < 0) {
290     std::cerr << "rbd: export-diff error: " << cpp_strerror(r) << std::endl;
291     return r;
292   }
293   return 0;
294 }
295
296 Shell::SwitchArguments switched_arguments({at::WHOLE_OBJECT});
297 Shell::Action action_diff(
298   {"export-diff"}, {}, "Export incremental diff to file.", "",
299   &get_arguments_diff, &execute_diff);
300
301 class C_Export : public Context
302 {
303 public:
304   C_Export(SimpleThrottle &simple_throttle, librbd::Image &image,
305            uint64_t fd_offset, uint64_t offset, uint64_t length, int fd)
306     : m_aio_completion(
307         new librbd::RBD::AioCompletion(this, &utils::aio_context_callback)),
308       m_throttle(simple_throttle), m_image(image), m_dest_offset(fd_offset),
309       m_offset(offset), m_length(length), m_fd(fd)
310   {
311   }
312
313   void send()
314   {
315     m_throttle.start_op();
316
317     int op_flags = LIBRADOS_OP_FLAG_FADVISE_SEQUENTIAL |
318                    LIBRADOS_OP_FLAG_FADVISE_NOCACHE;
319     int r = m_image.aio_read2(m_offset, m_length, m_bufferlist,
320                               m_aio_completion, op_flags);
321     if (r < 0) {
322       cerr << "rbd: error requesting read from source image" << std::endl;
323       m_aio_completion->release();
324       m_throttle.end_op(r);
325     }
326   }
327
328   void finish(int r) override
329   {
330     BOOST_SCOPE_EXIT((&m_throttle) (&r))
331     {
332       m_throttle.end_op(r);
333     } BOOST_SCOPE_EXIT_END
334
335     if (r < 0) {
336       cerr << "rbd: error reading from source image at offset "
337            << m_offset << ": " << cpp_strerror(r) << std::endl;
338       return;
339     }
340
341     assert(m_bufferlist.length() == static_cast<size_t>(r));
342     if (m_fd != STDOUT_FILENO) {
343       if (m_bufferlist.is_zero()) {
344         return;
345       }
346
347       uint64_t chkret = lseek64(m_fd, m_dest_offset, SEEK_SET);
348       if (chkret != m_dest_offset) {
349         cerr << "rbd: error seeking destination image to offset "
350              << m_dest_offset << std::endl;
351         r = -errno;
352         return;
353       }
354     }
355
356     r = m_bufferlist.write_fd(m_fd);
357     if (r < 0) {
358       cerr << "rbd: error writing to destination image at offset "
359            << m_dest_offset << std::endl;
360     }
361   }
362
363 private:
364   librbd::RBD::AioCompletion *m_aio_completion;
365   SimpleThrottle &m_throttle;
366   librbd::Image &m_image;
367   bufferlist m_bufferlist;
368   uint64_t m_dest_offset;
369   uint64_t m_offset;
370   uint64_t m_length;
371   int m_fd;
372 };
373
374 static int do_export_v2(librbd::Image& image, librbd::image_info_t &info, int fd,
375                         uint64_t period, int max_concurrent_ops, utils::ProgressContext &pc)
376 {
377   int r = 0;
378   // header
379   bufferlist bl;
380   bl.append(utils::RBD_IMAGE_BANNER_V2);
381
382   __u8 tag;
383   uint64_t length;
384   // encode order
385   tag = RBD_EXPORT_IMAGE_ORDER;
386   length = 8;
387   ::encode(tag, bl);
388   ::encode(length, bl);
389   ::encode(uint64_t(info.order), bl);
390
391   // encode features
392   tag = RBD_EXPORT_IMAGE_FEATURES;
393   uint64_t features;
394   image.features(&features);
395   length = 8;
396   ::encode(tag, bl);
397   ::encode(length, bl);
398   ::encode(features, bl);
399
400   // encode stripe_unit and stripe_count
401   tag = RBD_EXPORT_IMAGE_STRIPE_UNIT;
402   uint64_t stripe_unit;
403   stripe_unit = image.get_stripe_unit();
404   length = 8;
405   ::encode(tag, bl);
406   ::encode(length, bl);
407   ::encode(stripe_unit, bl);
408
409   tag = RBD_EXPORT_IMAGE_STRIPE_COUNT;
410   uint64_t stripe_count;
411   stripe_count = image.get_stripe_count();
412   length = 8;
413   ::encode(tag, bl);
414   ::encode(length, bl);
415   ::encode(stripe_count, bl);
416
417   // encode end tag
418   tag = RBD_EXPORT_IMAGE_END;
419   ::encode(tag, bl);
420
421   // write bl to fd.
422   r = bl.write_fd(fd);
423   if (r < 0) {
424     return r;
425   }
426
427   // header for snapshots
428   bl.clear();
429   bl.append(utils::RBD_IMAGE_DIFFS_BANNER_V2);
430
431   std::vector<librbd::snap_info_t> snaps;
432   r = image.snap_list(snaps);
433   if (r < 0) {
434     return r;
435   }
436
437   uint64_t diff_num = snaps.size() + 1;
438   ::encode(diff_num, bl);
439
440   r = bl.write_fd(fd);
441   if (r < 0) {
442     return r;
443   }
444
445   const char *last_snap = NULL;
446   for (size_t i = 0; i < snaps.size(); ++i) {
447     utils::snap_set(image, snaps[i].name.c_str());
448     r = do_export_diff_fd(image, last_snap, snaps[i].name.c_str(), false, fd, true, 2);
449     if (r < 0) {
450       return r;
451     }
452     pc.update_progress(i, snaps.size() + 1);
453     last_snap = snaps[i].name.c_str();
454   }
455   utils::snap_set(image, std::string(""));
456   r = do_export_diff_fd(image, last_snap, nullptr, false, fd, true, 2);
457   if (r < 0) {
458     return r;
459   }
460   pc.update_progress(snaps.size() + 1, snaps.size() + 1);
461   return r;
462 }
463
464 static int do_export_v1(librbd::Image& image, librbd::image_info_t &info, int fd,
465                         uint64_t period, int max_concurrent_ops, utils::ProgressContext &pc)
466 {
467   int r = 0;
468   size_t file_size = 0;
469   SimpleThrottle throttle(max_concurrent_ops, false);
470   for (uint64_t offset = 0; offset < info.size; offset += period) {
471     if (throttle.pending_error()) {
472       break;
473     }
474
475     uint64_t length = min(period, info.size - offset);
476     C_Export *ctx = new C_Export(throttle, image, file_size + offset, offset, length, fd);
477     ctx->send();
478
479     pc.update_progress(offset, info.size);
480   }
481
482   file_size += info.size;
483   r = throttle.wait_for_ret();
484   if (fd != 1) {
485     if (r >= 0) {
486       r = ftruncate(fd, file_size);
487       if (r < 0)
488         return r;
489
490       uint64_t chkret = lseek64(fd, file_size, SEEK_SET);
491       if (chkret != file_size)
492         r = errno;
493     }
494   }
495   return r;
496 }
497
498 static int do_export(librbd::Image& image, const char *path, bool no_progress, int export_format)
499 {
500   librbd::image_info_t info;
501   int64_t r = image.stat(info, sizeof(info));
502   if (r < 0)
503     return r;
504
505   int fd;
506   int max_concurrent_ops;
507   bool to_stdout = (strcmp(path, "-") == 0);
508   if (to_stdout) {
509     fd = STDOUT_FILENO;
510     max_concurrent_ops = 1;
511   } else {
512     max_concurrent_ops = g_conf->get_val<int64_t>("rbd_concurrent_management_ops");
513     fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0644);
514     if (fd < 0) {
515       return -errno;
516     }
517 #ifdef HAVE_POSIX_FADVISE
518     posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL);
519 #endif
520   }
521
522   utils::ProgressContext pc("Exporting image", no_progress);
523   uint64_t period = image.get_stripe_count() * (1ull << info.order);
524
525   if (export_format == 1)
526     r = do_export_v1(image, info, fd, period, max_concurrent_ops, pc);
527   else
528     r = do_export_v2(image, info, fd, period, max_concurrent_ops, pc);
529
530   if (r < 0)
531     pc.fail();
532   else
533     pc.finish();
534   if (!to_stdout)
535     close(fd);
536   return r;
537 }
538
539 void get_arguments(po::options_description *positional,
540                    po::options_description *options) {
541   at::add_image_or_snap_spec_options(positional, options,
542                                      at::ARGUMENT_MODIFIER_SOURCE);
543   at::add_path_options(positional, options,
544                        "export file (or '-' for stdout)");
545   at::add_no_progress_option(options);
546   at::add_export_format_option(options);
547 }
548
549 int execute(const po::variables_map &vm) {
550   size_t arg_index = 0;
551   std::string pool_name;
552   std::string image_name;
553   std::string snap_name;
554   int r = utils::get_pool_image_snapshot_names(
555     vm, at::ARGUMENT_MODIFIER_SOURCE, &arg_index, &pool_name, &image_name,
556     &snap_name, utils::SNAPSHOT_PRESENCE_PERMITTED,
557     utils::SPEC_VALIDATION_NONE);
558   if (r < 0) {
559     return r;
560   }
561
562   std::string path;
563   r = utils::get_path(vm, utils::get_positional_argument(vm, 1), &path);
564   if (r < 0) {
565     return r;
566   }
567
568   librados::Rados rados;
569   librados::IoCtx io_ctx;
570   librbd::Image image;
571   r = utils::init_and_open_image(pool_name, image_name, "", snap_name, true,
572                                  &rados, &io_ctx, &image);
573   if (r < 0) {
574     return r;
575   }
576   
577   int format = 1;
578   if (vm.count("export-format"))
579     format = vm["export-format"].as<uint64_t>();
580
581   r = do_export(image, path.c_str(), vm[at::NO_PROGRESS].as<bool>(), format);
582   if (r < 0) {
583     std::cerr << "rbd: export error: " << cpp_strerror(r) << std::endl;
584     return r;
585   }
586   return 0;
587 }
588
589 Shell::Action action(
590   {"export"}, {}, "Export image to file.", "", &get_arguments, &execute);
591
592 } // namespace export_full
593 } // namespace action
594 } // namespace rbd