]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
rbd: migrated existing command logic to new namespaces
authorJason Dillaman <dillaman@redhat.com>
Fri, 18 Sep 2015 20:45:32 +0000 (16:45 -0400)
committerJason Dillaman <dillaman@redhat.com>
Thu, 5 Nov 2015 21:11:53 +0000 (16:11 -0500)
No logic changes were introduced during the migration, only
syntax changes were necessary.

Signed-off-by: Jason Dillaman <dillaman@redhat.com>
26 files changed:
src/tools/rbd/action/BenchWrite.cc
src/tools/rbd/action/Children.cc
src/tools/rbd/action/Clone.cc
src/tools/rbd/action/Copy.cc
src/tools/rbd/action/Create.cc
src/tools/rbd/action/Diff.cc
src/tools/rbd/action/DiskUsage.cc
src/tools/rbd/action/Export.cc
src/tools/rbd/action/ExportDiff.cc
src/tools/rbd/action/Feature.cc
src/tools/rbd/action/Flatten.cc
src/tools/rbd/action/ImageMeta.cc
src/tools/rbd/action/Import.cc
src/tools/rbd/action/ImportDiff.cc
src/tools/rbd/action/Info.cc
src/tools/rbd/action/Kernel.cc
src/tools/rbd/action/List.cc
src/tools/rbd/action/Lock.cc
src/tools/rbd/action/MergeDiff.cc
src/tools/rbd/action/ObjectMap.cc
src/tools/rbd/action/Remove.cc
src/tools/rbd/action/Rename.cc
src/tools/rbd/action/Resize.cc
src/tools/rbd/action/Snap.cc
src/tools/rbd/action/Status.cc
src/tools/rbd/action/Watch.cc

index 928ce5419f575857e2be9905fe0f8cd296c848cd..d767c087991cac5c36d82ab39b8b4696b535500c 100644 (file)
@@ -6,7 +6,12 @@
 #include "tools/rbd/Utils.h"
 #include "common/errno.h"
 #include "common/strtol.h"
+#include "common/Cond.h"
+#include "common/Mutex.h"
 #include <iostream>
+#include <boost/accumulators/accumulators.hpp>
+#include <boost/accumulators/statistics/stats.hpp>
+#include <boost/accumulators/statistics/rolling_sum.hpp>
 #include <boost/program_options.hpp>
 
 namespace rbd {
@@ -50,6 +55,184 @@ void validate(boost::any& v, const std::vector<std::string>& values,
 
 } // anonymous namespace
 
+static void rbd_bencher_completion(void *c, void *pc);
+struct rbd_bencher;
+
+struct rbd_bencher {
+  librbd::Image *image;
+  Mutex lock;
+  Cond cond;
+  int in_flight;
+
+  rbd_bencher(librbd::Image *i)
+    : image(i),
+      lock("rbd_bencher::lock"),
+      in_flight(0)
+  { }
+
+  bool start_write(int max, uint64_t off, uint64_t len, bufferlist& bl,
+                   int op_flags)
+  {
+    {
+      Mutex::Locker l(lock);
+      if (in_flight >= max)
+        return false;
+      in_flight++;
+    }
+    librbd::RBD::AioCompletion *c =
+      new librbd::RBD::AioCompletion((void *)this, rbd_bencher_completion);
+    image->aio_write2(off, len, bl, c, op_flags);
+    //cout << "start " << c << " at " << off << "~" << len << std::endl;
+    return true;
+  }
+
+  void wait_for(int max) {
+    Mutex::Locker l(lock);
+    while (in_flight > max) {
+      utime_t dur;
+      dur.set_from_double(.2);
+      cond.WaitInterval(g_ceph_context, lock, dur);
+    }
+  }
+
+};
+
+void rbd_bencher_completion(void *vc, void *pc)
+{
+  librbd::RBD::AioCompletion *c = (librbd::RBD::AioCompletion *)vc;
+  rbd_bencher *b = static_cast<rbd_bencher *>(pc);
+  //cout << "complete " << c << std::endl;
+  int ret = c->get_return_value();
+  if (ret != 0) {
+    cout << "write error: " << cpp_strerror(ret) << std::endl;
+    assert(0 == ret);
+  }
+  b->lock.Lock();
+  b->in_flight--;
+  b->cond.Signal();
+  b->lock.Unlock();
+  c->release();
+}
+
+int do_bench_write(librbd::Image& image, uint64_t io_size,
+                   uint64_t io_threads, uint64_t io_bytes,
+                   bool random)
+{
+  rbd_bencher b(&image);
+
+  std::cout << "bench-write "
+       << " io_size " << io_size
+       << " io_threads " << io_threads
+       << " bytes " << io_bytes
+       << " pattern " << (random ? "random" : "sequential")
+       << std::endl;
+
+  srand(time(NULL) % (unsigned long) -1);
+
+  bufferptr bp(io_size);
+  memset(bp.c_str(), rand() & 0xff, io_size);
+  bufferlist bl;
+  bl.push_back(bp);
+
+  utime_t start = ceph_clock_now(NULL);
+  utime_t last;
+  unsigned ios = 0;
+
+  uint64_t size = 0;
+  image.size(&size);
+
+  vector<uint64_t> thread_offset;
+  uint64_t i;
+  uint64_t start_pos;
+
+  // disturb all thread's offset, used by seq write
+  for (i = 0; i < io_threads; i++) {
+    start_pos = (rand() % (size / io_size)) * io_size;
+    thread_offset.push_back(start_pos);
+  }
+
+  const int WINDOW_SIZE = 5;
+  typedef boost::accumulators::accumulator_set<
+    double, boost::accumulators::stats<
+      boost::accumulators::tag::rolling_sum> > RollingSum;
+
+  RollingSum time_acc(
+    boost::accumulators::tag::rolling_window::window_size = WINDOW_SIZE);
+  RollingSum ios_acc(
+    boost::accumulators::tag::rolling_window::window_size = WINDOW_SIZE);
+  RollingSum off_acc(
+    boost::accumulators::tag::rolling_window::window_size = WINDOW_SIZE);
+  uint64_t cur_ios = 0;
+  uint64_t cur_off = 0;
+
+  int op_flags;
+  if  (random) {
+    op_flags = LIBRADOS_OP_FLAG_FADVISE_RANDOM;
+  } else {
+    op_flags = LIBRADOS_OP_FLAG_FADVISE_SEQUENTIAL;
+  }
+
+  printf("  SEC       OPS   OPS/SEC   BYTES/SEC\n");
+  uint64_t off;
+  for (off = 0; off < io_bytes; ) {
+    b.wait_for(io_threads - 1);
+    i = 0;
+    while (i < io_threads && off < io_bytes) {
+      if (random) {
+        thread_offset[i] = (rand() % (size / io_size)) * io_size;
+      } else {
+        thread_offset[i] += io_size;
+        if (thread_offset[i] + io_size > size)
+          thread_offset[i] = 0;
+      }
+
+      if (!b.start_write(io_threads, thread_offset[i], io_size, bl, op_flags))
+        break;
+
+      ++i;
+      ++ios;
+      off += io_size;
+
+      ++cur_ios;
+      cur_off += io_size;
+    }
+
+    utime_t now = ceph_clock_now(NULL);
+    utime_t elapsed = now - start;
+    if (last.is_zero()) {
+      last = elapsed;
+    } else if (elapsed.sec() != last.sec()) {
+      time_acc(elapsed - last);
+      ios_acc(static_cast<double>(cur_ios));
+      off_acc(static_cast<double>(cur_off));
+      cur_ios = 0;
+      cur_off = 0;
+
+      double time_sum = boost::accumulators::rolling_sum(time_acc);
+      printf("%5d  %8d  %8.2lf  %8.2lf\n",
+             (int)elapsed,
+             (int)(ios - io_threads),
+             boost::accumulators::rolling_sum(ios_acc) / time_sum,
+             boost::accumulators::rolling_sum(off_acc) / time_sum);
+      last = elapsed;
+    }
+  }
+  b.wait_for(0);
+  int r = image.flush();
+  if (r < 0) {
+    std::cerr << "Error flushing data at the end: " << cpp_strerror(r)
+              << std::endl;
+  }
+
+  utime_t now = ceph_clock_now(NULL);
+  double elapsed = now - start;
+
+  printf("elapsed: %5d  ops: %8d  ops/sec: %8.2lf  bytes/sec: %8.2lf\n",
+         (int)elapsed, ios, (double)ios / elapsed, (double)off / elapsed);
+
+  return 0;
+}
+
 void get_arguments(po::options_description *positional,
                    po::options_description *options) {
   at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
@@ -110,6 +293,12 @@ int execute(const po::variables_map &vm) {
     return r;
   }
 
+  r = do_bench_write(image, bench_io_size, bench_io_threads, bench_bytes,
+                     bench_random);
+  if (r < 0) {
+    std::cerr << "bench-write failed: " << cpp_strerror(r) << std::endl;
+    return r;
+  }
   return 0;
 }
 
index f368a8717379aebf8d07deb08bb94ee163a616b7..b930eb90bc97b05b74481413e18f61aa7edd1f2e 100644 (file)
@@ -16,6 +16,37 @@ namespace children {
 namespace at = argument_types;
 namespace po = boost::program_options;
 
+int do_list_children(librbd::Image &image, Formatter *f)
+{
+  std::set<std::pair<std::string, std::string> > children;
+  int r;
+
+  r = image.list_children(&children);
+  if (r < 0)
+    return r;
+
+  if (f)
+    f->open_array_section("children");
+
+  for (auto &child_it : children) {
+    if (f) {
+      f->open_object_section("child");
+      f->dump_string("pool", child_it.first);
+      f->dump_string("image", child_it.second);
+      f->close_section();
+    } else {
+      std::cout << child_it.first << "/" << child_it.second << std::endl;
+    }
+  }
+
+  if (f) {
+    f->close_section();
+    f->flush(std::cout);
+  }
+
+  return 0;
+}
+
 void get_arguments(po::options_description *positional,
                    po::options_description *options) {
   at::add_snap_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
@@ -49,6 +80,12 @@ int execute(const po::variables_map &vm) {
     return r;
   }
 
+  r = do_list_children(image, formatter.get());
+  if (r < 0) {
+    std::cerr << "rbd: listing children failed: " << cpp_strerror(r)
+              << std::endl;
+    return r;
+  }
   return 0;
 }
 
index 08b6ddd5a260fdc4833d0fabd17dd32086935802..6c98433d7430a42edfb727a48ad9f06323a5191a 100644 (file)
@@ -15,6 +15,19 @@ namespace clone {
 namespace at = argument_types;
 namespace po = boost::program_options;
 
+int do_clone(librbd::RBD &rbd, librados::IoCtx &p_ioctx,
+             const char *p_name, const char *p_snapname,
+             librados::IoCtx &c_ioctx, const char *c_name,
+             uint64_t features, int *c_order,
+             uint64_t stripe_unit, uint64_t stripe_count) {
+  if ((features & RBD_FEATURE_LAYERING) != RBD_FEATURE_LAYERING) {
+    return -EINVAL;
+  }
+
+  return rbd.clone2(p_ioctx, p_name, p_snapname, c_ioctx, c_name, features,
+                    c_order, stripe_unit, stripe_count);
+}
+
 void get_arguments(po::options_description *positional,
                    po::options_description *options) {
   at::add_snap_spec_options(positional, options, at::ARGUMENT_MODIFIER_SOURCE);
@@ -67,6 +80,14 @@ int execute(const po::variables_map &vm) {
     return r;
   }
 
+  librbd::RBD rbd;
+  r = do_clone(rbd, io_ctx, image_name.c_str(), snap_name.c_str(), dst_io_ctx,
+               dst_image_name.c_str(), features, &order, stripe_unit,
+               stripe_count);
+  if (r < 0) {
+    std::cerr << "rbd: clone error: " << cpp_strerror(r) << std::endl;
+    return r;
+  }
   return 0;
 }
 
index 79369d9255ade1decabd8831b163609fa842e626..9275e4b33f1e1bf9314e3a801e0faf6d49b07c65 100644 (file)
@@ -15,6 +15,19 @@ namespace copy {
 namespace at = argument_types;
 namespace po = boost::program_options;
 
+static int do_copy(librbd::Image &src, librados::IoCtx& dest_pp,
+                   const char *destname, bool no_progress)
+{
+  utils::ProgressContext pc("Image copy", no_progress);
+  int r = src.copy_with_progress(dest_pp, destname, pc);
+  if (r < 0){
+    pc.fail();
+    return r;
+  }
+  pc.finish();
+  return 0;
+}
+
 void get_arguments(po::options_description *positional,
                    po::options_description *options) {
   at::add_image_or_snap_spec_options(positional, options,
@@ -60,6 +73,12 @@ int execute(const po::variables_map &vm) {
     return r;
   }
 
+  r = do_copy(image, dst_io_ctx, dst_image_name.c_str(),
+              vm[at::NO_PROGRESS].as<bool>());
+  if (r < 0) {
+    std::cerr << "rbd: copy failed: " << cpp_strerror(r) << std::endl;
+    return r;
+  }
   return 0;
 }
 
index 52285e2a867946a8b6bf6ca9d8920cca8843e7a9..49eedb6cc2a0db024b1a13a317c05cc8c751bfb3 100644 (file)
@@ -15,6 +15,23 @@ namespace create {
 namespace at = argument_types;
 namespace po = boost::program_options;
 
+static int do_create(librbd::RBD &rbd, librados::IoCtx& io_ctx,
+                     const char *imgname, uint64_t size, int *order,
+                     int format, uint64_t features,
+                     uint64_t stripe_unit, uint64_t stripe_count) {
+  int r;
+  if (format == 1) {
+    r = rbd.create(io_ctx, imgname, size, order);
+  } else {
+    r = rbd.create3(io_ctx, imgname, size, features, order,
+                    stripe_unit, stripe_count);
+  }
+  if (r < 0) {
+    return r;
+  }
+  return 0;
+}
+
 void get_arguments(po::options_description *positional,
                    po::options_description *options) {
   at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
@@ -58,6 +75,13 @@ int execute(const po::variables_map &vm) {
     return r;
   }
 
+  librbd::RBD rbd;
+  r = do_create(rbd, io_ctx, image_name.c_str(), size, &order, format, features,
+                stripe_unit, stripe_count);
+  if (r < 0) {
+    std::cerr << "rbd: create error: " << cpp_strerror(r) << std::endl;
+    return r;
+  }
   return 0;
 }
 
index 921e29687effe77a650400f25e20986e05a2db84..cd0aeb28936ab2a8c03e6817ccf50a989b7b68ad 100644 (file)
@@ -6,6 +6,7 @@
 #include "tools/rbd/Utils.h"
 #include "common/errno.h"
 #include "common/Formatter.h"
+#include "common/TextTable.h"
 #include <iostream>
 #include <boost/program_options.hpp>
 
@@ -16,6 +17,64 @@ namespace diff {
 namespace at = argument_types;
 namespace po = boost::program_options;
 
+struct output_method {
+  output_method() : f(NULL), t(NULL), empty(true) {}
+  Formatter *f;
+  TextTable *t;
+  bool empty;
+};
+
+static int diff_cb(uint64_t ofs, size_t len, int exists, void *arg)
+{
+  output_method *om = static_cast<output_method *>(arg);
+  om->empty = false;
+  if (om->f) {
+    om->f->open_object_section("extent");
+    om->f->dump_unsigned("offset", ofs);
+    om->f->dump_unsigned("length", len);
+    om->f->dump_string("exists", exists ? "true" : "false");
+    om->f->close_section();
+  } else {
+    assert(om->t);
+    *(om->t) << ofs << len << (exists ? "data" : "zero") << TextTable::endrow;
+  }
+  return 0;
+}
+
+static int do_diff(librbd::Image& image, const char *fromsnapname,
+                   bool whole_object, Formatter *f)
+{
+  int r;
+  librbd::image_info_t info;
+
+  r = image.stat(info, sizeof(info));
+  if (r < 0)
+    return r;
+
+  output_method om;
+  if (f) {
+    om.f = f;
+    f->open_array_section("extents");
+  } else {
+    om.t = new TextTable();
+    om.t->define_column("Offset", TextTable::LEFT, TextTable::LEFT);
+    om.t->define_column("Length", TextTable::LEFT, TextTable::LEFT);
+    om.t->define_column("Type", TextTable::LEFT, TextTable::LEFT);
+  }
+
+  r = image.diff_iterate2(fromsnapname, 0, info.size, true, whole_object,
+                          diff_cb, &om);
+  if (f) {
+    f->close_section();
+    f->flush(std::cout);
+  } else {
+    if (!om.empty)
+      std::cout << *om.t;
+    delete om.t;
+  }
+  return r;
+}
+
 void get_arguments(po::options_description *positional,
                    po::options_description *options) {
   at::add_image_or_snap_spec_options(positional, options,
@@ -61,6 +120,12 @@ int execute(const po::variables_map &vm) {
     return r;
   }
 
+  r = do_diff(image, from_snap_name.empty() ? nullptr : from_snap_name.c_str(),
+              diff_whole_object, formatter.get());
+  if (r < 0) {
+    std::cerr << "rbd: diff error: " << cpp_strerror(r) << std::endl;
+    return -r;
+  }
   return 0;
 }
 
index b2cd1cf27c2b03d511a9cf87cddbf7f0f3f6ff18..8e59ffeff807cb0bc27ae70e565554c5c0d9653c 100644 (file)
@@ -4,8 +4,14 @@
 #include "tools/rbd/ArgumentTypes.h"
 #include "tools/rbd/Shell.h"
 #include "tools/rbd/Utils.h"
+#include "include/types.h"
+#include "include/stringify.h"
+#include "common/errno.h"
 #include "common/Formatter.h"
+#include "common/TextTable.h"
+#include <algorithm>
 #include <iostream>
+#include <boost/bind.hpp>
 #include <boost/program_options.hpp>
 
 namespace rbd {
@@ -15,6 +21,199 @@ namespace disk_usage {
 namespace at = argument_types;
 namespace po = boost::program_options;
 
+static int disk_usage_callback(uint64_t offset, size_t len, int exists,
+                               void *arg) {
+  uint64_t *used_size = reinterpret_cast<uint64_t *>(arg);
+  if (exists) {
+    (*used_size) += len;
+  }
+  return 0;
+}
+
+static int compute_image_disk_usage(const std::string& name,
+                                    const std::string& snap_name,
+                                    const std::string& from_snap_name,
+                                    librbd::Image &image, uint64_t size,
+                                    TextTable& tbl, Formatter *f,
+                                    uint64_t *used_size) {
+  const char* from = NULL;
+  if (!from_snap_name.empty()) {
+    from = from_snap_name.c_str();
+  }
+
+  uint64_t flags;
+  int r = image.get_flags(&flags);
+  if (r < 0) {
+    std::cerr << "rbd: failed to retrieve image flags: " << cpp_strerror(r)
+         << std::endl;
+    return r;
+  }
+  if ((flags & RBD_FLAG_FAST_DIFF_INVALID) != 0) {
+    std::cerr << "warning: fast-diff map is invalid for " << name
+         << (snap_name.empty() ? "" : "@" + snap_name) << ". "
+         << "operation may be slow." << std::endl;
+  }
+
+  *used_size = 0;
+  r = image.diff_iterate2(from, 0, size, false, true,
+                          &disk_usage_callback, used_size);
+  if (r < 0) {
+    std::cerr << "rbd: failed to iterate diffs: " << cpp_strerror(r)
+              << std::endl;
+    return r;
+  }
+
+  if (f) {
+    f->open_object_section("image");
+    f->dump_string("name", name);
+    if (!snap_name.empty()) {
+      f->dump_string("snapshot", snap_name);
+    }
+    f->dump_unsigned("provisioned_size", size);
+    f->dump_unsigned("used_size" , *used_size);
+    f->close_section();
+  } else {
+    std::string full_name = name;
+    if (!snap_name.empty()) {
+      full_name += "@" + snap_name;
+    }
+    tbl << full_name
+        << stringify(si_t(size))
+        << stringify(si_t(*used_size))
+        << TextTable::endrow;
+  }
+  return 0;
+}
+
+static int do_disk_usage(librbd::RBD &rbd, librados::IoCtx &io_ctx,
+                        const char *imgname, const char *snapname,
+                        Formatter *f) {
+  std::vector<std::string> names;
+  int r = rbd.list(io_ctx, names);
+  if (r == -ENOENT) {
+    r = 0;
+  } else if (r < 0) {
+    return r;
+  }
+
+  TextTable tbl;
+  if (f) {
+    f->open_object_section("stats");
+    f->open_array_section("images");
+  } else {
+    tbl.define_column("NAME", TextTable::LEFT, TextTable::LEFT);
+    tbl.define_column("PROVISIONED", TextTable::RIGHT, TextTable::RIGHT);
+    tbl.define_column("USED", TextTable::RIGHT, TextTable::RIGHT);
+  }
+
+  uint64_t used_size = 0;
+  uint64_t total_prov = 0;
+  uint64_t total_used = 0;
+  std::sort(names.begin(), names.end());
+  for (std::vector<string>::const_iterator name = names.begin();
+       name != names.end(); ++name) {
+    if (imgname != NULL && *name != imgname) {
+      continue;
+    }
+
+    librbd::Image image;
+    r = rbd.open_read_only(io_ctx, image, name->c_str(), NULL);
+    if (r < 0) {
+      if (r != -ENOENT) {
+        std::cerr << "rbd: error opening " << *name << ": " << cpp_strerror(r)
+                  << std::endl;
+      }
+      continue;
+    }
+
+    uint64_t features;
+    int r = image.features(&features);
+    if (r < 0) {
+      std::cerr << "rbd: failed to retrieve image features: " << cpp_strerror(r)
+                << std::endl;
+      return r;
+    }
+    if ((features & RBD_FEATURE_FAST_DIFF) == 0) {
+      std::cerr << "warning: fast-diff map is not enabled for " << *name << ". "
+                << "operation may be slow." << std::endl;
+    }
+
+    librbd::image_info_t info;
+    if (image.stat(info, sizeof(info)) < 0) {
+      return -EINVAL;
+    }
+
+    std::vector<librbd::snap_info_t> snap_list;
+    r = image.snap_list(snap_list);
+    if (r < 0) {
+      std::cerr << "rbd: error opening " << *name << " snapshots: "
+                << cpp_strerror(r) << std::endl;
+      continue;
+    }
+
+    std::string last_snap_name;
+    std::sort(snap_list.begin(), snap_list.end(),
+              boost::bind(&librbd::snap_info_t::id, _1) <
+                boost::bind(&librbd::snap_info_t::id, _2));
+    for (std::vector<librbd::snap_info_t>::const_iterator snap =
+         snap_list.begin(); snap != snap_list.end(); ++snap) {
+      librbd::Image snap_image;
+      r = rbd.open_read_only(io_ctx, snap_image, name->c_str(),
+                             snap->name.c_str());
+      if (r < 0) {
+        std::cerr << "rbd: error opening snapshot " << *name << "@"
+                  << snap->name << ": " << cpp_strerror(r) << std::endl;
+        return r;
+      }
+
+      if (imgname == NULL || (snapname != NULL && snap->name == snapname)) {
+        r = compute_image_disk_usage(*name, snap->name, last_snap_name,
+                                     snap_image, snap->size, tbl, f,
+                                     &used_size);
+        if (r < 0) {
+          return r;
+        }
+
+        if (snapname != NULL) {
+          total_prov += snap->size;
+        }
+        total_used += used_size;
+      }
+      last_snap_name = snap->name;
+    }
+
+    if (snapname == NULL) {
+      r = compute_image_disk_usage(*name, "", last_snap_name, image, info.size,
+                                   tbl, f, &used_size);
+      if (r < 0) {
+        return r;
+      }
+      total_prov += info.size;
+      total_used += used_size;
+    }
+  }
+
+  if (f) {
+    f->close_section();
+    if (imgname == NULL) {
+      f->dump_unsigned("total_provisioned_size", total_prov);
+      f->dump_unsigned("total_used_size", total_used);
+    }
+    f->close_section();
+    f->flush(std::cout);
+  } else {
+    if (imgname == NULL) {
+      tbl << "<TOTAL>"
+          << stringify(si_t(total_prov))
+          << stringify(si_t(total_used))
+          << TextTable::endrow;
+    }
+    std::cout << tbl;
+  }
+
+  return 0;
+}
+
 void get_arguments(po::options_description *positional,
                    po::options_description *options) {
   at::add_image_or_snap_spec_options(positional, options,
@@ -48,6 +247,15 @@ int execute(const po::variables_map &vm) {
     return r;
   }
 
+  librbd::RBD rbd;
+  r = do_disk_usage(rbd, io_ctx,
+                    image_name.empty() ? nullptr: image_name.c_str() ,
+                    snap_name.empty() ? nullptr : snap_name.c_str(),
+                    formatter.get());
+  if (r < 0) {
+    std::cerr << "du failed: " << cpp_strerror(r) << std::endl;
+    return r;
+  }
   return 0;
 }
 
index 9f3b65d84f5f2bd86432598b32de6ee5a86e21c3..324a4b320dd5f5f7fbb5d0df346c2e7d44fe1284 100644 (file)
@@ -4,9 +4,12 @@
 #include "tools/rbd/ArgumentTypes.h"
 #include "tools/rbd/Shell.h"
 #include "tools/rbd/Utils.h"
+#include "include/Context.h"
 #include "common/errno.h"
+#include "common/Throttle.h"
 #include <iostream>
 #include <boost/program_options.hpp>
+#include <boost/scope_exit.hpp>
 
 namespace rbd {
 namespace action {
@@ -15,6 +18,132 @@ namespace export_full {
 namespace at = argument_types;
 namespace po = boost::program_options;
 
+class C_Export : public Context
+{
+public:
+  C_Export(SimpleThrottle &simple_throttle, librbd::Image &image,
+                   uint64_t offset, uint64_t length, int fd)
+    : m_aio_completion(
+        new librbd::RBD::AioCompletion(this, &utils::aio_context_callback)),
+      m_throttle(simple_throttle), m_image(image), m_offset(offset),
+      m_length(length), m_fd(fd)
+  {
+  }
+
+  void send()
+  {
+    m_throttle.start_op();
+
+    int op_flags = LIBRADOS_OP_FLAG_FADVISE_SEQUENTIAL |
+                   LIBRADOS_OP_FLAG_FADVISE_NOCACHE;
+    int r = m_image.aio_read2(m_offset, m_length, m_bufferlist,
+                              m_aio_completion, op_flags);
+    if (r < 0) {
+      cerr << "rbd: error requesting read from source image" << std::endl;
+      m_aio_completion->release();
+      m_throttle.end_op(r);
+    }
+  }
+
+  virtual void finish(int r)
+  {
+    BOOST_SCOPE_EXIT((&m_throttle) (&r))
+    {
+      m_throttle.end_op(r);
+    } BOOST_SCOPE_EXIT_END
+
+    if (r < 0) {
+      cerr << "rbd: error reading from source image at offset "
+           << m_offset << ": " << cpp_strerror(r) << std::endl;
+      return;
+    }
+
+    assert(m_bufferlist.length() == static_cast<size_t>(r));
+    if (m_fd != STDOUT_FILENO) {
+      if (m_bufferlist.is_zero()) {
+        return;
+      }
+
+      uint64_t chkret = lseek64(m_fd, m_offset, SEEK_SET);
+      if (chkret != m_offset) {
+        cerr << "rbd: error seeking destination image to offset "
+             << m_offset << std::endl;
+        r = -errno;
+        return;
+      }
+    }
+
+    r = m_bufferlist.write_fd(m_fd);
+    if (r < 0) {
+      cerr << "rbd: error writing to destination image at offset "
+           << m_offset << std::endl;
+    }
+  }
+
+private:
+  librbd::RBD::AioCompletion *m_aio_completion;
+  SimpleThrottle &m_throttle;
+  librbd::Image &m_image;
+  bufferlist m_bufferlist;
+  uint64_t m_offset;
+  uint64_t m_length;
+  int m_fd;
+};
+
+static int do_export(librbd::Image& image, const char *path, bool no_progress)
+{
+  librbd::image_info_t info;
+  int64_t r = image.stat(info, sizeof(info));
+  if (r < 0)
+    return r;
+
+  int fd;
+  int max_concurrent_ops;
+  bool to_stdout = (strcmp(path, "-") == 0);
+  if (to_stdout) {
+    fd = STDOUT_FILENO;
+    max_concurrent_ops = 1;
+  } else {
+    max_concurrent_ops = max(g_conf->rbd_concurrent_management_ops, 1);
+    fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0644);
+    if (fd < 0) {
+      return -errno;
+    }
+    posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL);
+  }
+
+  utils::ProgressContext pc("Exporting image", no_progress);
+
+  SimpleThrottle throttle(max_concurrent_ops, false);
+  uint64_t period = image.get_stripe_count() * (1ull << info.order);
+  for (uint64_t offset = 0; offset < info.size; offset += period) {
+    if (throttle.pending_error()) {
+      break;
+    }
+
+    uint64_t length = min(period, info.size - offset);
+    C_Export *ctx = new C_Export(throttle, image, offset, length, fd);
+    ctx->send();
+
+    pc.update_progress(offset, info.size);
+  }
+
+  r = throttle.wait_for_ret();
+  if (!to_stdout) {
+    if (r >= 0) {
+      r = ftruncate(fd, info.size);
+    }
+    close(fd);
+  }
+
+  if (r < 0) {
+    pc.fail();
+  } else {
+    pc.finish();
+  }
+  return r;
+}
+
 void get_arguments(po::options_description *positional,
                    po::options_description *options) {
   at::add_image_or_snap_spec_options(positional, options,
@@ -51,6 +180,11 @@ int execute(const po::variables_map &vm) {
     return r;
   }
 
+  r = do_export(image, path.c_str(), vm[at::NO_PROGRESS].as<bool>());
+  if (r < 0) {
+    std::cerr << "rbd: export error: " << cpp_strerror(r) << std::endl;
+    return r;
+  }
   return 0;
 }
 
index 2b061ee02e497f257ad2301045cc8cf60ef7b2e0..245bbf3ab0b4e12ea1a7b9f38fd554ff7ac61996 100644 (file)
@@ -4,9 +4,14 @@
 #include "tools/rbd/ArgumentTypes.h"
 #include "tools/rbd/Shell.h"
 #include "tools/rbd/Utils.h"
+#include "include/encoding.h"
 #include "common/errno.h"
+#include "common/Throttle.h"
+#include <fcntl.h>
 #include <iostream>
+#include <stdlib.h>
 #include <boost/program_options.hpp>
+#include <boost/scope_exit.hpp>
 
 namespace rbd {
 namespace action {
@@ -15,6 +20,179 @@ namespace export_diff {
 namespace at = argument_types;
 namespace po = boost::program_options;
 
+struct ExportDiffContext {
+  librbd::Image *image;
+  int fd;
+  uint64_t totalsize;
+  utils::ProgressContext pc;
+  OrderedThrottle throttle;
+
+  ExportDiffContext(librbd::Image *i, int f, uint64_t t, int max_ops,
+                    bool no_progress) :
+    image(i), fd(f), totalsize(t), pc("Exporting image", no_progress),
+    throttle(max_ops, true) {
+  }
+};
+
+class C_ExportDiff : public Context {
+public:
+  C_ExportDiff(ExportDiffContext *edc, uint64_t offset, uint64_t length,
+               bool exists)
+    : m_export_diff_context(edc), m_offset(offset), m_length(length),
+      m_exists(exists) {
+  }
+
+  int send() {
+    if (m_export_diff_context->throttle.pending_error()) {
+      return m_export_diff_context->throttle.wait_for_ret();
+    }
+
+    C_OrderedThrottle *ctx = m_export_diff_context->throttle.start_op(this);
+    if (m_exists) {
+      librbd::RBD::AioCompletion *aio_completion =
+        new librbd::RBD::AioCompletion(ctx, &utils::aio_context_callback);
+
+      int op_flags = LIBRADOS_OP_FLAG_FADVISE_NOCACHE;
+      int r = m_export_diff_context->image->aio_read2(
+        m_offset, m_length, m_read_data, aio_completion, op_flags);
+      if (r < 0) {
+        aio_completion->release();
+        ctx->complete(r);
+      }
+    } else {
+      ctx->complete(0);
+    }
+    return 0;
+  }
+
+  static int export_diff_cb(uint64_t offset, size_t length, int exists,
+                            void *arg) {
+    ExportDiffContext *edc = reinterpret_cast<ExportDiffContext *>(arg);
+
+    C_ExportDiff *context = new C_ExportDiff(edc, offset, length, exists);
+    return context->send();
+  }
+
+protected:
+  virtual void finish(int r) {
+    if (r >= 0) {
+      if (m_exists) {
+        m_exists = !m_read_data.is_zero();
+      }
+      r = write_extent(m_export_diff_context, m_offset, m_length, m_exists);
+      if (r == 0 && m_exists) {
+        r = m_read_data.write_fd(m_export_diff_context->fd);
+      }
+    }
+    m_export_diff_context->throttle.end_op(r);
+  }
+
+private:
+  ExportDiffContext *m_export_diff_context;
+  uint64_t m_offset;
+  uint64_t m_length;
+  bool m_exists;
+  bufferlist m_read_data;
+
+  static int write_extent(ExportDiffContext *edc, uint64_t offset,
+                          uint64_t length, bool exists) {
+    // extent
+    bufferlist bl;
+    __u8 tag = exists ? 'w' : 'z';
+    ::encode(tag, bl);
+    ::encode(offset, bl);
+    ::encode(length, bl);
+    int r = bl.write_fd(edc->fd);
+
+    edc->pc.update_progress(offset, edc->totalsize);
+    return r;
+  }
+};
+
+static int do_export_diff(librbd::Image& image, const char *fromsnapname,
+                          const char *endsnapname, bool whole_object,
+                          const char *path, bool no_progress)
+{
+  int r;
+  librbd::image_info_t info;
+  int fd;
+
+  r = image.stat(info, sizeof(info));
+  if (r < 0)
+    return r;
+
+  if (strcmp(path, "-") == 0)
+    fd = 1;
+  else
+    fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0644);
+  if (fd < 0)
+    return -errno;
+
+  BOOST_SCOPE_EXIT((&r) (&fd) (&path)) {
+    close(fd);
+    if (r < 0 && fd != 1) {
+      remove(path);
+    }
+  } BOOST_SCOPE_EXIT_END
+
+  {
+    // header
+    bufferlist bl;
+    bl.append(utils::RBD_DIFF_BANNER);
+
+    __u8 tag;
+    if (fromsnapname) {
+      tag = 'f';
+      ::encode(tag, bl);
+      std::string from(fromsnapname);
+      ::encode(from, bl);
+    }
+
+    if (endsnapname) {
+      tag = 't';
+      ::encode(tag, bl);
+      std::string to(endsnapname);
+      ::encode(to, bl);
+    }
+
+    tag = 's';
+    ::encode(tag, bl);
+    uint64_t endsize = info.size;
+    ::encode(endsize, bl);
+
+    r = bl.write_fd(fd);
+    if (r < 0) {
+      return r;
+    }
+  }
+  ExportDiffContext edc(&image, fd, info.size,
+                        g_conf->rbd_concurrent_management_ops, no_progress);
+  r = image.diff_iterate2(fromsnapname, 0, info.size, true, whole_object,
+                          &C_ExportDiff::export_diff_cb, (void *)&edc);
+  if (r < 0) {
+    goto out;
+  }
+
+  r = edc.throttle.wait_for_ret();
+  if (r < 0) {
+    goto out;
+  }
+
+  {
+    __u8 tag = 'e';
+    bufferlist bl;
+    ::encode(tag, bl);
+    r = bl.write_fd(fd);
+  }
+
+ out:
+  if (r < 0)
+    edc.pc.fail();
+  else
+    edc.pc.finish();
+  return r;
+}
+
 void get_arguments(po::options_description *positional,
                    po::options_description *options) {
   at::add_image_or_snap_spec_options(positional, options,
@@ -60,6 +238,15 @@ int execute(const po::variables_map &vm) {
     return r;
   }
 
+  r = do_export_diff(image,
+                     from_snap_name.empty() ? nullptr : from_snap_name.c_str(),
+                     snap_name.empty() ? nullptr : snap_name.c_str(),
+                     vm[at::WHOLE_OBJECT].as<bool>(), path.c_str(),
+                     vm[at::NO_PROGRESS].as<bool>());
+  if (r < 0) {
+    std::cerr << "rbd: export-diff error: " << cpp_strerror(r) << std::endl;
+    return r;
+  }
   return 0;
 }
 
index 2255b82bfd5ef268fbfea2e26c21ccb57a5fc400..4bd61a6aa9a8146cb5da82dd86a5ab13f70d3d57 100644 (file)
@@ -57,6 +57,12 @@ int execute(const po::variables_map &vm, bool enabled) {
     return r;
   }
 
+  r = image.update_features(boost::any_cast<uint64_t>(features_any), enabled);
+  if (r < 0) {
+    std::cerr << "rbd: failed to update image features: " << cpp_strerror(r)
+              << std::endl;
+    return r;
+  }
   return 0;
 }
 
index a8d096c8ffb853bdd4f35227fa221100aa8e58ca..51225437aec387e4513e1ee8dc30b5daaef18dc5 100644 (file)
@@ -15,6 +15,18 @@ namespace flatten {
 namespace at = argument_types;
 namespace po = boost::program_options;
 
+static int do_flatten(librbd::Image& image, bool no_progress)
+{
+  utils::ProgressContext pc("Image flatten", no_progress);
+  int r = image.flatten_with_progress(pc);
+  if (r < 0) {
+    pc.fail();
+    return r;
+  }
+  pc.finish();
+  return 0;
+}
+
 void get_arguments(po::options_description *positional,
                    po::options_description *options) {
   at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
@@ -42,6 +54,11 @@ int execute(const po::variables_map &vm) {
     return r;
   }
 
+  r = do_flatten(image, vm[at::NO_PROGRESS].as<bool>());
+  if (r < 0) {
+    std::cerr << "rbd: flatten error: " << cpp_strerror(r) << std::endl;
+    return r;
+  }
   return 0;
 }
 
index 28fc1e5b1a46c65d487a9739ceb53786ab01b45b..2cf1a25a2ebaa7d95e14933000ad5c73865b79d8 100644 (file)
@@ -6,6 +6,7 @@
 #include "tools/rbd/Utils.h"
 #include "common/errno.h"
 #include "common/Formatter.h"
+#include "common/TextTable.h"
 #include <iostream>
 #include <boost/program_options.hpp>
 
@@ -34,6 +35,88 @@ int get_key(const po::variables_map &vm, std::string *key) {
 
 } // anonymous namespace
 
+static int do_metadata_list(librbd::Image& image, Formatter *f)
+{
+  std::map<std::string, bufferlist> pairs;
+  int r;
+  TextTable tbl;
+
+  r = image.metadata_list("", 0, &pairs);
+  if (r < 0) {
+    std::cerr << "failed to list metadata of image : " << cpp_strerror(r)
+              << std::endl;
+    return r;
+  }
+
+  if (f) {
+    f->open_object_section("metadatas");
+  } else {
+    tbl.define_column("Key", TextTable::LEFT, TextTable::LEFT);
+    tbl.define_column("Value", TextTable::LEFT, TextTable::LEFT);
+  }
+
+  if (!pairs.empty()) {
+    bool one = (pairs.size() == 1);
+
+    if (!f) {
+      std::cout << "There " << (one ? "is " : "are ") << pairs.size()
+           << " metadata" << (one ? "" : "s") << " on this image.\n";
+    }
+
+    for (std::map<std::string, bufferlist>::iterator it = pairs.begin();
+         it != pairs.end(); ++it) {
+      std::string val(it->second.c_str(), it->second.length());
+      if (f) {
+        f->dump_string(it->first.c_str(), val.c_str());
+      } else {
+        tbl << it->first << val.c_str() << TextTable::endrow;
+      }
+    }
+    if (!f)
+      std::cout << tbl;
+  }
+
+  if (f) {
+    f->close_section();
+    f->flush(std::cout);
+  }
+  return 0;
+}
+
+static int do_metadata_set(librbd::Image& image, const char *key,
+                          const char *value)
+{
+  int r = image.metadata_set(key, value);
+  if (r < 0) {
+    std::cerr << "failed to set metadata " << key << " of image : "
+              << cpp_strerror(r) << std::endl;
+  }
+  return r;
+}
+
+static int do_metadata_remove(librbd::Image& image, const char *key)
+{
+  int r = image.metadata_remove(key);
+  if (r < 0) {
+    std::cerr << "failed to remove metadata " << key << " of image : "
+              << cpp_strerror(r) << std::endl;
+  }
+  return r;
+}
+
+static int do_metadata_get(librbd::Image& image, const char *key)
+{
+  std::string s;
+  int r = image.metadata_get(key, &s);
+  if (r < 0) {
+    std::cerr << "failed to get metadata " << key << " of image : "
+              << cpp_strerror(r) << std::endl;
+    return r;
+  }
+  std::cout << s << std::endl;
+  return r;
+}
+
 void get_list_arguments(po::options_description *positional,
                         po::options_description *options) {
   at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
@@ -67,6 +150,12 @@ int execute_list(const po::variables_map &vm) {
     return r;
   }
 
+  r = do_metadata_list(image, formatter.get());
+  if (r < 0) {
+    std::cerr << "rbd: listing metadata failed: " << cpp_strerror(r)
+              << std::endl;
+    return r;
+  }
   return 0;
 }
 
@@ -103,6 +192,12 @@ int execute_get(const po::variables_map &vm) {
     return r;
   }
 
+  r = do_metadata_get(image, key.c_str());
+  if (r < 0) {
+    std::cerr << "rbd: getting metadata failed: " << cpp_strerror(r)
+              << std::endl;
+    return r;
+  }
   return 0;
 }
 
@@ -147,6 +242,12 @@ int execute_set(const po::variables_map &vm) {
     return r;
   }
 
+  r = do_metadata_set(image, key.c_str(), value.c_str());
+  if (r < 0) {
+    std::cerr << "rbd: setting metadata failed: " << cpp_strerror(r)
+              << std::endl;
+    return r;
+  }
   return 0;
 }
 
@@ -183,6 +284,12 @@ int execute_remove(const po::variables_map &vm) {
     return r;
   }
 
+  r = do_metadata_remove(image, key.c_str());
+  if (r < 0) {
+    std::cerr << "rbd: removing metadata failed: " << cpp_strerror(r)
+              << std::endl;
+    return r;
+  }
   return 0;
 }
 
index f5dd141fe831d6d12c3a98fd03d399c4bbf695d1..bb7cb7d86711fb781b5971678209a35b856f84c5 100644 (file)
@@ -4,9 +4,13 @@
 #include "tools/rbd/ArgumentTypes.h"
 #include "tools/rbd/Shell.h"
 #include "tools/rbd/Utils.h"
+#include "include/Context.h"
+#include "common/blkdev.h"
 #include "common/errno.h"
+#include "common/Throttle.h"
 #include <iostream>
 #include <boost/program_options.hpp>
+#include <boost/scoped_ptr.hpp>
 
 namespace rbd {
 namespace action {
@@ -15,6 +19,213 @@ namespace import {
 namespace at = argument_types;
 namespace po = boost::program_options;
 
+class C_Import : public Context {
+public:
+  C_Import(SimpleThrottle &simple_throttle, librbd::Image &image,
+           bufferlist &bl, uint64_t offset)
+    : m_throttle(simple_throttle), m_image(image),
+      m_aio_completion(
+        new librbd::RBD::AioCompletion(this, &utils::aio_context_callback)),
+      m_bufferlist(bl), m_offset(offset)
+  {
+  }
+
+  void send()
+  {
+    m_throttle.start_op();
+
+    int op_flags = LIBRADOS_OP_FLAG_FADVISE_SEQUENTIAL |
+                   LIBRADOS_OP_FLAG_FADVISE_NOCACHE;
+    int r = m_image.aio_write2(m_offset, m_bufferlist.length(), m_bufferlist,
+                               m_aio_completion, op_flags);
+    if (r < 0) {
+      std::cerr << "rbd: error requesting write to destination image"
+                << std::endl;
+      m_aio_completion->release();
+      m_throttle.end_op(r);
+    }
+  }
+
+  virtual void finish(int r)
+  {
+    if (r < 0) {
+      std::cerr << "rbd: error writing to destination image at offset "
+                << m_offset << ": " << cpp_strerror(r) << std::endl;
+    }
+    m_throttle.end_op(r);
+  }
+
+private:
+  SimpleThrottle &m_throttle;
+  librbd::Image &m_image;
+  librbd::RBD::AioCompletion *m_aio_completion;
+  bufferlist m_bufferlist;
+  uint64_t m_offset;
+};
+
+static int do_import(librbd::RBD &rbd, librados::IoCtx& io_ctx,
+                     const char *imgname, int *order, const char *path,
+                     int format, uint64_t features,
+                     uint64_t stripe_unit, uint64_t stripe_count,
+                     bool no_progress)
+{
+  int fd, r;
+  struct stat stat_buf;
+  utils::ProgressContext pc("Importing image", no_progress);
+
+  assert(imgname);
+
+  // default order as usual
+  if (*order == 0)
+    *order = 22;
+
+  // try to fill whole imgblklen blocks for sparsification
+  uint64_t image_pos = 0;
+  size_t imgblklen = 1 << *order;
+  char *p = new char[imgblklen];
+  size_t reqlen = imgblklen;    // amount requested from read
+  ssize_t readlen;              // amount received from one read
+  size_t blklen = 0;            // amount accumulated from reads to fill blk
+  librbd::Image image;
+  uint64_t size = 0;
+
+  boost::scoped_ptr<SimpleThrottle> throttle;
+  bool from_stdin = !strcmp(path, "-");
+  if (from_stdin) {
+    throttle.reset(new SimpleThrottle(1, false));
+    fd = 0;
+    size = 1ULL << *order;
+  } else {
+    throttle.reset(new SimpleThrottle(
+      max(g_conf->rbd_concurrent_management_ops, 1), false));
+    if ((fd = open(path, O_RDONLY)) < 0) {
+      r = -errno;
+      std::cerr << "rbd: error opening " << path << std::endl;
+      goto done2;
+    }
+
+    if ((fstat(fd, &stat_buf)) < 0) {
+      r = -errno;
+      std::cerr << "rbd: stat error " << path << std::endl;
+      goto done;
+    }
+    if (S_ISDIR(stat_buf.st_mode)) {
+      r = -EISDIR;
+      std::cerr << "rbd: cannot import a directory" << std::endl;
+      goto done;
+    }
+    if (stat_buf.st_size)
+      size = (uint64_t)stat_buf.st_size;
+
+    if (!size) {
+      int64_t bdev_size = 0;
+      r = get_block_device_size(fd, &bdev_size);
+      if (r < 0) {
+        std::cerr << "rbd: unable to get size of file/block device"
+                  << std::endl;
+        goto done;
+      }
+      assert(bdev_size >= 0);
+      size = (uint64_t) bdev_size;
+    }
+
+    posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL);
+  }
+
+  if (format == 1) {
+    // weird striping not allowed with format 1!
+    if ((stripe_unit || stripe_count) &&
+        (stripe_unit != (1ull << *order) && stripe_count != 1)) {
+      std::cerr << "non-default striping not allowed with format 1; "
+                << "use --image-format 2" << std::endl;
+      return -EINVAL;
+    }
+    r = rbd.create(io_ctx, imgname, size, order);
+  } else {
+    r = rbd.create3(io_ctx, imgname, size, features, order,
+                    stripe_unit, stripe_count);
+  }
+  if (r < 0) {
+    std::cerr << "rbd: image creation failed" << std::endl;
+    goto done;
+  }
+
+  r = rbd.open(io_ctx, image, imgname);
+  if (r < 0) {
+    std::cerr << "rbd: failed to open image" << std::endl;
+    goto done;
+  }
+
+  // loop body handles 0 return, as we may have a block to flush
+  while ((readlen = ::read(fd, p + blklen, reqlen)) >= 0) {
+    if (throttle->pending_error()) {
+      break;
+    }
+
+    blklen += readlen;
+    // if read was short, try again to fill the block before writing
+    if (readlen && ((size_t)readlen < reqlen)) {
+      reqlen -= readlen;
+      continue;
+    }
+    if (!from_stdin)
+      pc.update_progress(image_pos, size);
+
+    bufferlist bl(blklen);
+    bl.append(p, blklen);
+    // resize output image by binary expansion as we go for stdin
+    if (from_stdin && (image_pos + (size_t)blklen) > size) {
+      size *= 2;
+      r = image.resize(size);
+      if (r < 0) {
+        std::cerr << "rbd: can't resize image during import" << std::endl;
+        goto done;
+      }
+    }
+
+    // write as much as we got; perhaps less than imgblklen
+    // but skip writing zeros to create sparse images
+    if (!bl.is_zero()) {
+      C_Import *ctx = new C_Import(*throttle, image, bl, image_pos);
+      ctx->send();
+    }
+
+    // done with whole block, whether written or not
+    image_pos += blklen;
+    // if read had returned 0, we're at EOF and should quit
+    if (readlen == 0)
+      break;
+    blklen = 0;
+    reqlen = imgblklen;
+  }
+  r = throttle->wait_for_ret();
+  if (r < 0) {
+    goto done;
+  }
+
+  if (from_stdin) {
+    r = image.resize(image_pos);
+    if (r < 0) {
+      std::cerr << "rbd: final image resize failed" << std::endl;
+      goto done;
+    }
+  }
+
+  r = image.close();
+
+ done:
+  if (!from_stdin) {
+    if (r < 0)
+      pc.fail();
+    else
+      pc.finish();
+    close(fd);
+  }
+ done2:
+  delete[] p;
+  return r;
+}
+
 void get_arguments(po::options_description *positional,
                    po::options_description *options) {
   at::add_path_options(positional, options,
@@ -87,6 +298,15 @@ int execute(const po::variables_map &vm) {
     return r;
   }
 
+  librbd::RBD rbd;
+  r = do_import(rbd, io_ctx, image_name.c_str(), &order, path.c_str(),
+                format, features, stripe_unit, stripe_count,
+                vm[at::NO_PROGRESS].as<bool>());
+  if (r < 0) {
+    std::cerr << "rbd: import failed: " << cpp_strerror(r) << std::endl;
+    return r;
+  }
+
   return 0;
 }
 
index 82e0a83ce4872238943608538983f5466065842e..9f600f8285d8d149d0f69f2940a561628e1d8ec9 100644 (file)
@@ -4,7 +4,10 @@
 #include "tools/rbd/ArgumentTypes.h"
 #include "tools/rbd/Shell.h"
 #include "tools/rbd/Utils.h"
+#include "include/encoding.h"
+#include "common/debug.h"
 #include "common/errno.h"
+#include "common/safe_io.h"
 #include <iostream>
 #include <boost/program_options.hpp>
 
@@ -17,6 +20,157 @@ namespace import_diff {
 namespace at = argument_types;
 namespace po = boost::program_options;
 
+static int do_import_diff(librbd::Image &image, const char *path,
+                          bool no_progress)
+{
+  int fd, r;
+  struct stat stat_buf;
+  utils::ProgressContext pc("Importing image diff", no_progress);
+  uint64_t size = 0;
+  uint64_t off = 0;
+  string from, to;
+  char buf[utils::RBD_DIFF_BANNER.size() + 1];
+
+  bool from_stdin = !strcmp(path, "-");
+  if (from_stdin) {
+    fd = 0;
+  } else {
+    fd = open(path, O_RDONLY);
+    if (fd < 0) {
+      r = -errno;
+      std::cerr << "rbd: error opening " << path << std::endl;
+      return r;
+    }
+    r = ::fstat(fd, &stat_buf);
+    if (r < 0)
+      goto done;
+    size = (uint64_t)stat_buf.st_size;
+  }
+
+  r = safe_read_exact(fd, buf, utils::RBD_DIFF_BANNER.size());
+  if (r < 0)
+    goto done;
+  buf[utils::RBD_DIFF_BANNER.size()] = '\0';
+  if (strcmp(buf, utils::RBD_DIFF_BANNER.c_str())) {
+    std::cerr << "invalid banner '" << buf << "', expected '"
+              << utils::RBD_DIFF_BANNER << "'" << std::endl;
+    r = -EINVAL;
+    goto done;
+  }
+
+  while (true) {
+    __u8 tag;
+    r = safe_read_exact(fd, &tag, 1);
+    if (r < 0) {
+      goto done;
+    }
+
+    if (tag == 'e') {
+      dout(2) << " end diff" << dendl;
+      break;
+    } else if (tag == 'f') {
+      r = utils::read_string(fd, 4096, &from);   // 4k limit to make sure we don't get a garbage string
+      if (r < 0)
+        goto done;
+      dout(2) << " from snap " << from << dendl;
+
+      if (!image.snap_exists(from.c_str())) {
+        std::cerr << "start snapshot '" << from
+                  << "' does not exist in the image, aborting" << std::endl;
+        r = -EINVAL;
+        goto done;
+      }
+    }
+    else if (tag == 't') {
+      r = utils::read_string(fd, 4096, &to);   // 4k limit to make sure we don't get a garbage string
+      if (r < 0)
+        goto done;
+      dout(2) << "   to snap " << to << dendl;
+
+      // verify this snap isn't already present
+      if (image.snap_exists(to.c_str())) {
+        std::cerr << "end snapshot '" << to
+                  << "' already exists, aborting" << std::endl;
+        r = -EEXIST;
+        goto done;
+      }
+    } else if (tag == 's') {
+      uint64_t end_size;
+      char buf[8];
+      r = safe_read_exact(fd, buf, 8);
+      if (r < 0)
+        goto done;
+      bufferlist bl;
+      bl.append(buf, 8);
+      bufferlist::iterator p = bl.begin();
+      ::decode(end_size, p);
+      uint64_t cur_size;
+      image.size(&cur_size);
+      if (cur_size != end_size) {
+        dout(2) << "resize " << cur_size << " -> " << end_size << dendl;
+        image.resize(end_size);
+      } else {
+        dout(2) << "size " << end_size << " (no change)" << dendl;
+      }
+      if (from_stdin)
+        size = end_size;
+    } else if (tag == 'w' || tag == 'z') {
+      uint64_t len;
+      char buf[16];
+      r = safe_read_exact(fd, buf, 16);
+      if (r < 0)
+        goto done;
+      bufferlist bl;
+      bl.append(buf, 16);
+      bufferlist::iterator p = bl.begin();
+      ::decode(off, p);
+      ::decode(len, p);
+
+      if (tag == 'w') {
+        bufferptr bp = buffer::create(len);
+        r = safe_read_exact(fd, bp.c_str(), len);
+        if (r < 0)
+          goto done;
+        bufferlist data;
+        data.append(bp);
+        dout(2) << " write " << off << "~" << len << dendl;
+        image.write2(off, len, data, LIBRADOS_OP_FLAG_FADVISE_NOCACHE);
+      } else {
+        dout(2) << " zero " << off << "~" << len << dendl;
+        image.discard(off, len);
+      }
+    } else {
+      std::cerr << "unrecognized tag byte " << (int)tag
+                << " in stream; aborting" << std::endl;
+      r = -EINVAL;
+      goto done;
+    }
+    if (!from_stdin) {
+      // progress through input
+      uint64_t off = lseek64(fd, 0, SEEK_CUR);
+      pc.update_progress(off, size);
+    } else if (size) {
+      // progress through image offsets.  this may jitter if blocks
+      // aren't in order, but it is better than nothing.
+      pc.update_progress(off, size);
+    }
+  }
+  // take final snap
+  if (to.length()) {
+    dout(2) << " create end snap " << to << dendl;
+    r = image.snap_create(to.c_str());
+  }
+
+ done:
+  if (r < 0)
+    pc.fail();
+  else
+    pc.finish();
+  if (!from_stdin)
+    close(fd);
+  return r;
+}
+
 void get_arguments(po::options_description *positional,
                    po::options_description *options) {
   at::add_path_options(positional, options,
@@ -52,6 +206,11 @@ int execute(const po::variables_map &vm) {
     return r;
   }
 
+  r = do_import_diff(image, path.c_str(), vm[at::NO_PROGRESS].as<bool>());
+  if (r < 0) {
+    cerr << "rbd: import-diff failed: " << cpp_strerror(r) << std::endl;
+    return r;
+  }
   return 0;
 }
 
index 023951f549d342bab0a7ee91825d9e40f6c49248..76e394049f415e2ff5c09b398a0bd9742b03963d 100644 (file)
@@ -4,6 +4,8 @@
 #include "tools/rbd/ArgumentTypes.h"
 #include "tools/rbd/Shell.h"
 #include "tools/rbd/Utils.h"
+#include "include/types.h"
+#include "common/errno.h"
 #include "common/Formatter.h"
 #include <iostream>
 #include <boost/program_options.hpp>
@@ -15,6 +17,168 @@ namespace info {
 namespace at = argument_types;
 namespace po = boost::program_options;
 
+static void format_bitmask(Formatter *f, const std::string &name,
+                           const std::map<uint64_t, std::string>& mapping,
+                           uint64_t bitmask)
+{
+  int count = 0;
+  std::string group_name(name + "s");
+  if (f == NULL) {
+    std::cout << "\t" << group_name << ": ";
+  } else {
+    f->open_array_section(group_name.c_str());
+  }
+  for (std::map<uint64_t, std::string>::const_iterator it = mapping.begin();
+       it != mapping.end(); ++it) {
+    if ((it->first & bitmask) == 0) {
+      continue;
+    }
+
+    if (f == NULL) {
+      if (count++ > 0) {
+        std::cout << ", ";
+      }
+      std::cout << it->second;
+    } else {
+      f->dump_string(name.c_str(), it->second);
+    }
+  }
+  if (f == NULL) {
+    std::cout << std::endl;
+  } else {
+    f->close_section();
+  }
+}
+
+static void format_features(Formatter *f, uint64_t features)
+{
+  format_bitmask(f, "feature", at::ImageFeatures::FEATURE_MAPPING, features);
+}
+
+static void format_flags(Formatter *f, uint64_t flags)
+{
+  std::map<uint64_t, std::string> mapping = {
+    {RBD_FLAG_OBJECT_MAP_INVALID, "object map invalid"},
+    {RBD_FLAG_FAST_DIFF_INVALID, "fast diff invalid"}};
+  format_bitmask(f, "flag", mapping, flags);
+}
+
+static int do_show_info(const char *imgname, librbd::Image& image,
+                        const char *snapname, Formatter *f)
+{
+  librbd::image_info_t info;
+  std::string parent_pool, parent_name, parent_snapname;
+  uint8_t old_format;
+  uint64_t overlap, features, flags;
+  bool snap_protected = false;
+  int r;
+
+  r = image.stat(info, sizeof(info));
+  if (r < 0)
+    return r;
+
+  r = image.old_format(&old_format);
+  if (r < 0)
+    return r;
+
+  r = image.overlap(&overlap);
+  if (r < 0)
+    return r;
+
+  r = image.features(&features);
+  if (r < 0)
+    return r;
+
+  r = image.get_flags(&flags);
+  if (r < 0) {
+    return r;
+  }
+
+  if (snapname) {
+    r = image.snap_is_protected(snapname, &snap_protected);
+    if (r < 0)
+      return r;
+  }
+
+  char prefix[RBD_MAX_BLOCK_NAME_SIZE + 1];
+  strncpy(prefix, info.block_name_prefix, RBD_MAX_BLOCK_NAME_SIZE);
+  prefix[RBD_MAX_BLOCK_NAME_SIZE] = '\0';
+
+  if (f) {
+    f->open_object_section("image");
+    f->dump_string("name", imgname);
+    f->dump_unsigned("size", info.size);
+    f->dump_unsigned("objects", info.num_objs);
+    f->dump_int("order", info.order);
+    f->dump_unsigned("object_size", info.obj_size);
+    f->dump_string("block_name_prefix", prefix);
+    f->dump_int("format", (old_format ? 1 : 2));
+  } else {
+    std::cout << "rbd image '" << imgname << "':\n"
+              << "\tsize " << prettybyte_t(info.size) << " in "
+              << info.num_objs << " objects"
+              << std::endl
+              << "\torder " << info.order
+              << " (" << prettybyte_t(info.obj_size) << " objects)"
+              << std::endl
+              << "\tblock_name_prefix: " << prefix
+              << std::endl
+              << "\tformat: " << (old_format ? "1" : "2")
+              << std::endl;
+  }
+
+  if (!old_format) {
+    format_features(f, features);
+    format_flags(f, flags);
+  }
+
+  // snapshot info, if present
+  if (snapname) {
+    if (f) {
+      f->dump_string("protected", snap_protected ? "true" : "false");
+    } else {
+      std::cout << "\tprotected: " << (snap_protected ? "True" : "False")
+                << std::endl;
+    }
+  }
+
+  // parent info, if present
+  if ((image.parent_info(&parent_pool, &parent_name, &parent_snapname) == 0) &&
+      parent_name.length() > 0) {
+    if (f) {
+      f->open_object_section("parent");
+      f->dump_string("pool", parent_pool);
+      f->dump_string("image", parent_name);
+      f->dump_string("snapshot", parent_snapname);
+      f->dump_unsigned("overlap", overlap);
+      f->close_section();
+    } else {
+      std::cout << "\tparent: " << parent_pool << "/" << parent_name
+                << "@" << parent_snapname << std::endl;
+      std::cout << "\toverlap: " << prettybyte_t(overlap) << std::endl;
+    }
+  }
+
+  // striping info, if feature is set
+  if (features & RBD_FEATURE_STRIPINGV2) {
+    if (f) {
+      f->dump_unsigned("stripe_unit", image.get_stripe_unit());
+      f->dump_unsigned("stripe_count", image.get_stripe_count());
+    } else {
+      std::cout << "\tstripe unit: " << prettybyte_t(image.get_stripe_unit())
+                << std::endl
+                << "\tstripe count: " << image.get_stripe_count() << std::endl;
+    }
+  }
+
+  if (f) {
+    f->close_section();
+    f->flush(std::cout);
+  }
+
+  return 0;
+}
+
 void get_arguments(po::options_description *positional,
                    po::options_description *options) {
   at::add_image_or_snap_spec_options(positional, options,
@@ -49,6 +213,13 @@ int execute(const po::variables_map &vm) {
     return r;
   }
 
+  r = do_show_info(image_name.c_str(), image,
+                   snap_name.empty() ? nullptr : snap_name.c_str(),
+                   formatter.get());
+  if (r < 0) {
+    std::cerr << "rbd: info: " << cpp_strerror(r) << std::endl;
+    return r;
+  }
   return 0;
 }
 
index 846b5f594ca8953950dec5230808489057dfbe51..5daa925bf3c5ff435b9570ea2b237378137e800f 100644 (file)
@@ -4,12 +4,19 @@
 #include "tools/rbd/ArgumentTypes.h"
 #include "tools/rbd/Shell.h"
 #include "tools/rbd/Utils.h"
+#include "include/krbd.h"
+#include "include/stringify.h"
+#include "include/uuid.h"
+#include "common/config.h"
 #include "common/errno.h"
+#include "common/strtol.h"
 #include "common/Formatter.h"
+#include "msg/msg_types.h"
+#include "global/global_context.h"
 #include <iostream>
 #include <boost/algorithm/string/predicate.hpp>
-#include <boost/program_options.hpp>
 #include <boost/scope_exit.hpp>
+#include <boost/program_options.hpp>
 
 namespace rbd {
 namespace action {
@@ -18,6 +25,189 @@ namespace kernel {
 namespace at = argument_types;
 namespace po = boost::program_options;
 
+namespace {
+
+std::map<std::string, std::string> map_options;
+
+} // anonymous namespace
+
+static std::string map_option_uuid_cb(const char *value_char)
+{
+  uuid_d u;
+  if (!u.parse(value_char))
+    return "";
+
+  return stringify(u);
+}
+
+static std::string map_option_ip_cb(const char *value_char)
+{
+  entity_addr_t a;
+  const char *endptr;
+  if (!a.parse(value_char, &endptr) ||
+      endptr != value_char + strlen(value_char)) {
+    return "";
+  }
+
+  return stringify(a.addr);
+}
+
+static std::string map_option_int_cb(const char *value_char)
+{
+  std::string err;
+  int d = strict_strtol(value_char, 10, &err);
+  if (!err.empty() || d < 0)
+    return "";
+
+  return stringify(d);
+}
+
+static void put_map_option(const std::string key, std::string val)
+{
+  map_options[key] = val;
+}
+
+static int put_map_option_value(const std::string opt, const char *value_char,
+                                std::string (*parse_cb)(const char *))
+{
+  if (!value_char || *value_char == '\0') {
+    std::cerr << "rbd: " << opt << " option requires a value" << std::endl;
+    return -EINVAL;
+  }
+
+  std::string value = parse_cb(value_char);
+  if (value.empty()) {
+    std::cerr << "rbd: invalid " << opt << " value '" << value_char << "'"
+              << std::endl;
+    return -EINVAL;
+  }
+
+  put_map_option(opt, opt + "=" + value);
+  return 0;
+}
+
+static int parse_map_options(char *options)
+{
+  for (char *this_char = strtok(options, ", ");
+       this_char != NULL;
+       this_char = strtok(NULL, ",")) {
+    char *value_char;
+
+    if ((value_char = strchr(this_char, '=')) != NULL)
+      *value_char++ = '\0';
+
+    if (!strcmp(this_char, "fsid")) {
+      if (put_map_option_value("fsid", value_char, map_option_uuid_cb))
+        return -EINVAL;
+    } else if (!strcmp(this_char, "ip")) {
+      if (put_map_option_value("ip", value_char, map_option_ip_cb))
+        return -EINVAL;
+    } else if (!strcmp(this_char, "share") || !strcmp(this_char, "noshare")) {
+      put_map_option("share", this_char);
+    } else if (!strcmp(this_char, "crc") || !strcmp(this_char, "nocrc")) {
+      put_map_option("crc", this_char);
+    } else if (!strcmp(this_char, "cephx_require_signatures") ||
+               !strcmp(this_char, "nocephx_require_signatures")) {
+      put_map_option("cephx_require_signatures", this_char);
+    } else if (!strcmp(this_char, "tcp_nodelay") ||
+               !strcmp(this_char, "notcp_nodelay")) {
+      put_map_option("tcp_nodelay", this_char);
+    } else if (!strcmp(this_char, "mount_timeout")) {
+      if (put_map_option_value("mount_timeout", value_char, map_option_int_cb))
+        return -EINVAL;
+    } else if (!strcmp(this_char, "osdkeepalive")) {
+      if (put_map_option_value("osdkeepalive", value_char, map_option_int_cb))
+        return -EINVAL;
+    } else if (!strcmp(this_char, "osd_idle_ttl")) {
+      if (put_map_option_value("osd_idle_ttl", value_char, map_option_int_cb))
+        return -EINVAL;
+    } else if (!strcmp(this_char, "rw") || !strcmp(this_char, "ro")) {
+      put_map_option("rw", this_char);
+    } else if (!strcmp(this_char, "queue_depth")) {
+      if (put_map_option_value("queue_depth", value_char, map_option_int_cb))
+        return -EINVAL;
+    } else {
+      std::cerr << "rbd: unknown map option '" << this_char << "'" << std::endl;
+      return -EINVAL;
+    }
+  }
+
+  return 0;
+}
+
+static int do_kernel_showmapped(Formatter *f)
+{
+  struct krbd_ctx *krbd;
+  int r;
+
+  r = krbd_create_from_context(g_ceph_context, &krbd);
+  if (r < 0)
+    return r;
+
+  r = krbd_showmapped(krbd, f);
+
+  krbd_destroy(krbd);
+  return r;
+}
+
+static int do_kernel_map(const char *poolname, const char *imgname,
+                         const char *snapname)
+{
+  struct krbd_ctx *krbd;
+  std::ostringstream oss;
+  char *devnode;
+  int r;
+
+  r = krbd_create_from_context(g_ceph_context, &krbd);
+  if (r < 0)
+    return r;
+
+  for (std::map<std::string, std::string>::iterator it = map_options.begin();
+       it != map_options.end(); ) {
+    // for compatibility with < 3.7 kernels, assume that rw is on by
+    // default and omit it even if it was specified by the user
+    // (see ceph.git commit fb0f1986449b)
+    if (it->first == "rw" && it->second == "rw") {
+      map_options.erase(it);
+    } else {
+      if (it != map_options.begin())
+        oss << ",";
+      oss << it->second;
+      ++it;
+    }
+  }
+
+  r = krbd_map(krbd, poolname, imgname, snapname, oss.str().c_str(), &devnode);
+  if (r < 0)
+    goto out;
+
+  std::cout << devnode << std::endl;
+
+  free(devnode);
+out:
+  krbd_destroy(krbd);
+  return r;
+}
+
+static int do_kernel_unmap(const char *dev, const char *poolname,
+                           const char *imgname, const char *snapname)
+{
+  struct krbd_ctx *krbd;
+  int r;
+
+  r = krbd_create_from_context(g_ceph_context, &krbd);
+  if (r < 0)
+    return r;
+
+  if (dev)
+    r = krbd_unmap(krbd, dev);
+  else
+    r = krbd_unmap_by_spec(krbd, poolname, imgname, snapname);
+
+  krbd_destroy(krbd);
+  return r;
+}
+
 void get_show_arguments(po::options_description *positional,
                         po::options_description *options) {
   at::add_format_options(options);
@@ -32,6 +222,11 @@ int execute_show(const po::variables_map &vm) {
 
   utils::init_context();
 
+  r = do_kernel_showmapped(formatter.get());
+  if (r < 0) {
+    std::cerr << "rbd: showmapped failed: " << cpp_strerror(r) << std::endl;
+    return r;
+  }
   return 0;
 }
 
@@ -56,8 +251,41 @@ int execute_map(const po::variables_map &vm) {
     return r;
   }
 
+  if (vm["read-only"].as<bool>()) {
+    put_map_option("rw", "ro");
+  }
+
+  // parse default options first so they can be overwritten by cli options
+  char *default_map_options = strdup(g_conf->rbd_default_map_options.c_str());
+  BOOST_SCOPE_EXIT( (default_map_options) ) {
+    free(default_map_options);
+  } BOOST_SCOPE_EXIT_END;
+
+  if (parse_map_options(default_map_options)) {
+    std::cerr << "rbd: couldn't parse default map options" << std::endl;
+    return -EINVAL;
+  }
+
+  if (vm.count("options")) {
+    char *cli_map_options = strdup(vm["options"].as<std::string>().c_str());
+    BOOST_SCOPE_EXIT( (cli_map_options) ) {
+      free(cli_map_options);
+    } BOOST_SCOPE_EXIT_END;
+
+    if (parse_map_options(cli_map_options)) {
+      std::cerr << "rbd: couldn't parse map options" << std::endl;
+      return -EINVAL;
+    }
+  }
+
   utils::init_context();
 
+  r = do_kernel_map(pool_name.c_str(), image_name.c_str(), snap_name.c_str());
+  if (r < 0) {
+    std::cerr << "rbd: map failed: " << cpp_strerror(r) << std::endl;
+    return r;
+  }
+
   return 0;
 }
 
@@ -101,6 +329,13 @@ int execute_unmap(const po::variables_map &vm) {
 
   utils::init_context();
 
+  r = do_kernel_unmap(device_name.empty() ? nullptr : device_name.c_str(),
+                      pool_name.c_str(), image_name.c_str(),
+                      snap_name.empty() ? nullptr : snap_name.c_str());
+  if (r < 0) {
+    std::cerr << "rbd: unmap failed: " << cpp_strerror(r) << std::endl;
+    return r;
+  }
   return 0;
 }
 
index f176d7b7c73742115a65ee60d7c5ae6e74d601f0..6b2041ac204a9ab1cbec21e8a95763d95de2260c 100644 (file)
@@ -4,8 +4,11 @@
 #include "tools/rbd/ArgumentTypes.h"
 #include "tools/rbd/Shell.h"
 #include "tools/rbd/Utils.h"
+#include "include/stringify.h"
+#include "include/types.h"
 #include "common/errno.h"
 #include "common/Formatter.h"
+#include "common/TextTable.h"
 #include <iostream>
 #include <boost/program_options.hpp>
 
@@ -16,6 +19,170 @@ namespace list {
 namespace at = argument_types;
 namespace po = boost::program_options;
 
+int do_list(librbd::RBD &rbd, librados::IoCtx& io_ctx, bool lflag,
+                   Formatter *f) {
+  std::vector<std::string> names;
+  int r = rbd.list(io_ctx, names);
+  if (r == -ENOENT)
+    r = 0;
+  if (r < 0)
+    return r;
+
+  if (!lflag) {
+    if (f)
+      f->open_array_section("images");
+    for (std::vector<std::string>::const_iterator i = names.begin();
+       i != names.end(); ++i) {
+       if (f)
+         f->dump_string("name", *i);
+       else
+         std::cout << *i << std::endl;
+    }
+    if (f) {
+      f->close_section();
+      f->flush(std::cout);
+    }
+    return 0;
+  }
+
+  TextTable tbl;
+
+  if (f) {
+    f->open_array_section("images");
+  } else {
+    tbl.define_column("NAME", TextTable::LEFT, TextTable::LEFT);
+    tbl.define_column("SIZE", TextTable::RIGHT, TextTable::RIGHT);
+    tbl.define_column("PARENT", TextTable::LEFT, TextTable::LEFT);
+    tbl.define_column("FMT", TextTable::RIGHT, TextTable::RIGHT);
+    tbl.define_column("PROT", TextTable::LEFT, TextTable::LEFT);
+    tbl.define_column("LOCK", TextTable::LEFT, TextTable::LEFT);
+  }
+
+  std::string pool, image, snap, parent;
+
+  for (std::vector<std::string>::const_iterator i = names.begin();
+       i != names.end(); ++i) {
+    librbd::image_info_t info;
+    librbd::Image im;
+
+    r = rbd.open_read_only(io_ctx, im, i->c_str(), NULL);
+    // image might disappear between rbd.list() and rbd.open(); ignore
+    // that, warn about other possible errors (EPERM, say, for opening
+    // an old-format image, because you need execute permission for the
+    // class method)
+    if (r < 0) {
+      if (r != -ENOENT) {
+        std::cerr << "rbd: error opening " << *i << ": " << cpp_strerror(r)
+                  << std::endl;
+      }
+      // in any event, continue to next image
+      continue;
+    }
+
+    // handle second-nth trips through loop
+    parent.clear();
+    r = im.parent_info(&pool, &image, &snap);
+    if (r < 0 && r != -ENOENT)
+      return r;
+    bool has_parent = false;
+    if (r != -ENOENT) {
+      parent = pool + "/" + image + "@" + snap;
+      has_parent = true;
+    }
+
+    if (im.stat(info, sizeof(info)) < 0)
+      return -EINVAL;
+
+    uint8_t old_format;
+    im.old_format(&old_format);
+
+    std::list<librbd::locker_t> lockers;
+    bool exclusive;
+    r = im.list_lockers(&lockers, &exclusive, NULL);
+    if (r < 0)
+      return r;
+    std::string lockstr;
+    if (!lockers.empty()) {
+      lockstr = (exclusive) ? "excl" : "shr";
+    }
+
+    if (f) {
+      f->open_object_section("image");
+      f->dump_string("image", *i);
+      f->dump_unsigned("size", info.size);
+      if (has_parent) {
+        f->open_object_section("parent");
+        f->dump_string("pool", pool);
+        f->dump_string("image", image);
+        f->dump_string("snapshot", snap);
+        f->close_section();
+      }
+      f->dump_int("format", old_format ? 1 : 2);
+      if (!lockers.empty())
+        f->dump_string("lock_type", exclusive ? "exclusive" : "shared");
+      f->close_section();
+    } else {
+      tbl << *i
+          << stringify(si_t(info.size))
+          << parent
+          << ((old_format) ? '1' : '2')
+          << ""                         // protect doesn't apply to images
+          << lockstr
+          << TextTable::endrow;
+    }
+
+    std::vector<librbd::snap_info_t> snaplist;
+    if (im.snap_list(snaplist) >= 0 && !snaplist.empty()) {
+      for (std::vector<librbd::snap_info_t>::iterator s = snaplist.begin();
+           s != snaplist.end(); ++s) {
+        bool is_protected;
+        bool has_parent = false;
+        parent.clear();
+        im.snap_set(s->name.c_str());
+        r = im.snap_is_protected(s->name.c_str(), &is_protected);
+        if (r < 0)
+          return r;
+        if (im.parent_info(&pool, &image, &snap) >= 0) {
+          parent = pool + "/" + image + "@" + snap;
+          has_parent = true;
+        }
+        if (f) {
+          f->open_object_section("snapshot");
+          f->dump_string("image", *i);
+          f->dump_string("snapshot", s->name);
+          f->dump_unsigned("size", s->size);
+          if (has_parent) {
+            f->open_object_section("parent");
+            f->dump_string("pool", pool);
+            f->dump_string("image", image);
+            f->dump_string("snapshot", snap);
+            f->close_section();
+          }
+          f->dump_int("format", old_format ? 1 : 2);
+          f->dump_string("protected", is_protected ? "true" : "false");
+          f->close_section();
+        } else {
+          tbl << *i + "@" + s->name
+              << stringify(si_t(s->size))
+              << parent
+              << ((old_format) ? '1' : '2')
+              << (is_protected ? "yes" : "")
+              << ""                     // locks don't apply to snaps
+              << TextTable::endrow;
+        }
+      }
+    }
+  }
+  if (f) {
+    f->close_section();
+    f->flush(std::cout);
+  } else if (!names.empty()) {
+    std::cout << tbl;
+  }
+
+  return 0;
+}
+
 void get_arguments(po::options_description *positional,
                    po::options_description *options) {
   positional->add_options()
@@ -49,6 +216,13 @@ int execute(const po::variables_map &vm) {
     return r;
   }
 
+  librbd::RBD rbd;
+  r = do_list(rbd, io_ctx, vm["long"].as<bool>(), formatter.get());
+  if (r < 0) {
+    std::cerr << "rbd: list: " << cpp_strerror(r) << std::endl;
+    return r;
+  }
+
   return 0;
 }
 
index ac0ae4010fca1ea0f0289681109f0ddb8d4b10fb..2b5eb29ea23fe2fb9a7b5164c57ce88f815c20b6 100644 (file)
@@ -4,7 +4,9 @@
 #include "tools/rbd/ArgumentTypes.h"
 #include "tools/rbd/Shell.h"
 #include "tools/rbd/Utils.h"
+#include "common/errno.h"
 #include "common/Formatter.h"
+#include "common/TextTable.h"
 #include <iostream>
 #include <boost/program_options.hpp>
 
@@ -33,6 +35,74 @@ int get_id(const po::variables_map &vm, std::string *id) {
 
 } // anonymous namespace
 
+static int do_lock_list(librbd::Image& image, Formatter *f)
+{
+  std::list<librbd::locker_t> lockers;
+  bool exclusive;
+  std::string tag;
+  TextTable tbl;
+  int r;
+
+  r = image.list_lockers(&lockers, &exclusive, &tag);
+  if (r < 0)
+    return r;
+
+  if (f) {
+    f->open_object_section("locks");
+  } else {
+    tbl.define_column("Locker", TextTable::LEFT, TextTable::LEFT);
+    tbl.define_column("ID", TextTable::LEFT, TextTable::LEFT);
+    tbl.define_column("Address", TextTable::LEFT, TextTable::LEFT);
+  }
+
+  if (lockers.size()) {
+    bool one = (lockers.size() == 1);
+
+    if (!f) {
+      std::cout << "There " << (one ? "is " : "are ") << lockers.size()
+           << (exclusive ? " exclusive" : " shared")
+           << " lock" << (one ? "" : "s") << " on this image.\n";
+      if (!exclusive)
+        std::cout << "Lock tag: " << tag << "\n";
+    }
+
+    for (std::list<librbd::locker_t>::const_iterator it = lockers.begin();
+         it != lockers.end(); ++it) {
+      if (f) {
+        f->open_object_section(it->cookie.c_str());
+        f->dump_string("locker", it->client);
+        f->dump_string("address", it->address);
+        f->close_section();
+      } else {
+        tbl << it->client << it->cookie << it->address << TextTable::endrow;
+      }
+    }
+    if (!f)
+      std::cout << tbl;
+  }
+
+  if (f) {
+    f->close_section();
+    f->flush(std::cout);
+  }
+  return 0;
+}
+
+static int do_lock_add(librbd::Image& image, const char *cookie,
+                       const char *tag)
+{
+  if (tag)
+    return image.lock_shared(cookie, tag);
+  else
+    return image.lock_exclusive(cookie);
+}
+
+static int do_lock_remove(librbd::Image& image, const char *client,
+                          const char *cookie)
+{
+  return image.break_lock(client, cookie);
+}
+
 void get_list_arguments(po::options_description *positional,
                         po::options_description *options) {
   at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
@@ -66,6 +136,11 @@ int execute_list(const po::variables_map &vm) {
     return r;
   }
 
+  r = do_lock_list(image, formatter.get());
+  if (r < 0) {
+    std::cerr << "rbd: listing locks failed: " << cpp_strerror(r) << std::endl;
+    return r;
+  }
   return 0;
 }
 
@@ -109,6 +184,21 @@ int execute_add(const po::variables_map &vm) {
     return r;
   }
 
+  r = do_lock_add(image, lock_cookie.c_str(),
+                  lock_tag.empty() ? nullptr : lock_tag.c_str());
+  if (r < 0) {
+    if (r == -EBUSY || r == -EEXIST) {
+      if (!lock_tag.empty()) {
+        std::cerr << "rbd: lock is alrady held by someone else"
+                  << " with a different tag" << std::endl;
+      } else {
+        std::cerr << "rbd: lock is already held by someone else" << std::endl;
+      }
+    } else {
+      std::cerr << "rbd: taking lock failed: " << cpp_strerror(r) << std::endl;
+    }
+    return r;
+  }
   return 0;
 }
 
@@ -153,6 +243,11 @@ int execute_remove(const po::variables_map &vm) {
     return r;
   }
 
+  r = do_lock_remove(image, lock_client.c_str(), lock_cookie.c_str());
+  if (r < 0) {
+    std::cerr << "rbd: releasing lock failed: " << cpp_strerror(r) << std::endl;
+    return r;
+  }
   return 0;
 }
 
index b06276f7bc883c2f8f7f290e4a2e878e8bd11df6..9e08a3729f09d9fe36957f07302c43c9e78a6947 100644 (file)
@@ -4,6 +4,8 @@
 #include "tools/rbd/ArgumentTypes.h"
 #include "tools/rbd/Shell.h"
 #include "tools/rbd/Utils.h"
+#include "common/safe_io.h"
+#include "common/debug.h"
 #include "common/errno.h"
 #include <iostream>
 #include <boost/program_options.hpp>
@@ -17,6 +19,374 @@ namespace merge_diff {
 namespace at = argument_types;
 namespace po = boost::program_options;
 
+static int parse_diff_header(int fd, __u8 *tag, string *from, string *to, uint64_t *size)
+{
+  int r;
+
+  {//header
+    char buf[utils::RBD_DIFF_BANNER.size() + 1];
+    r = safe_read_exact(fd, buf, utils::RBD_DIFF_BANNER.size());
+    if (r < 0)
+      return r;
+
+    buf[utils::RBD_DIFF_BANNER.size()] = '\0';
+    if (strcmp(buf, utils::RBD_DIFF_BANNER.c_str())) {
+      std::cerr << "invalid banner '" << buf << "', expected '"
+                << utils::RBD_DIFF_BANNER << "'" << std::endl;
+      return -EINVAL;
+    }
+  }
+
+  while (true) {
+    r = safe_read_exact(fd, tag, 1);
+    if (r < 0)
+      return r;
+
+    if (*tag == 'f') {
+      r = utils::read_string(fd, 4096, from);   // 4k limit to make sure we don't get a garbage string
+      if (r < 0)
+        return r;
+      dout(2) << " from snap " << *from << dendl;
+    } else if (*tag == 't') {
+      r = utils::read_string(fd, 4096, to);   // 4k limit to make sure we don't get a garbage string
+      if (r < 0)
+        return r;
+      dout(2) << " to snap " << *to << dendl;
+    } else if (*tag == 's') {
+      char buf[8];
+      r = safe_read_exact(fd, buf, 8);
+      if (r < 0)
+        return r;
+
+      bufferlist bl;
+      bl.append(buf, 8);
+      bufferlist::iterator p = bl.begin();
+      ::decode(*size, p);
+    } else {
+      break;
+    }
+  }
+
+  return 0;
+}
+
+static int parse_diff_body(int fd, __u8 *tag, uint64_t *offset, uint64_t *length)
+{
+  int r;
+
+  if (!(*tag)) {
+    r = safe_read_exact(fd, tag, 1);
+    if (r < 0)
+      return r;
+  }
+
+  if (*tag == 'e') {
+    offset = 0;
+    length = 0;
+    return 0;
+  }
+
+  if (*tag != 'w' && *tag != 'z')
+    return -ENOTSUP;
+
+  char buf[16];
+  r = safe_read_exact(fd, buf, 16);
+  if (r < 0)
+    return r;
+
+  bufferlist bl;
+  bl.append(buf, 16);
+  bufferlist::iterator p = bl.begin();
+  ::decode(*offset, p);
+  ::decode(*length, p);
+
+  if (!(*length))
+    return -ENOTSUP;
+
+  return 0;
+}
+
+/*
+ * fd: the diff file to read from
+ * pd: the diff file to be written into
+ */
+static int accept_diff_body(int fd, int pd, __u8 tag, uint64_t offset, uint64_t length)
+{
+  if (tag == 'e')
+    return 0;
+
+  bufferlist bl;
+  ::encode(tag, bl);
+  ::encode(offset, bl);
+  ::encode(length, bl);
+  int r;
+  r = bl.write_fd(pd);
+  if (r < 0)
+    return r;
+
+  if (tag == 'w') {
+    bufferptr bp = buffer::create(length);
+    r = safe_read_exact(fd, bp.c_str(), length);
+    if (r < 0)
+      return r;
+    bufferlist data;
+    data.append(bp);
+    r = data.write_fd(pd);
+    if (r < 0)
+      return r;
+  }
+
+  return 0;
+}
+
+/*
+ * Merge two diff files into one single file
+ * Note: It does not do the merging work if
+ * either of the source diff files is stripped,
+ * since which complicates the process and is
+ * rarely used
+ */
+static int do_merge_diff(const char *first, const char *second,
+                         const char *path, bool no_progress)
+{
+  utils::ProgressContext pc("Merging image diff", no_progress);
+  int fd = -1, sd = -1, pd = -1, r;
+
+  string f_from, f_to;
+  string s_from, s_to;
+  uint64_t f_size, s_size, pc_size;
+
+  __u8 f_tag = 0, s_tag = 0;
+  uint64_t f_off = 0, f_len = 0;
+  uint64_t s_off = 0, s_len = 0;
+  bool f_end = false, s_end = false;
+
+  bool first_stdin = !strcmp(first, "-");
+  if (first_stdin) {
+    fd = 0;
+  } else {
+    fd = open(first, O_RDONLY);
+    if (fd < 0) {
+      r = -errno;
+      std::cerr << "rbd: error opening " << first << std::endl;
+      goto done;
+    }
+  }
+
+  sd = open(second, O_RDONLY);
+  if (sd < 0) {
+    r = -errno;
+    std::cerr << "rbd: error opening " << second << std::endl;
+    goto done;
+  }
+
+  if (strcmp(path, "-") == 0) {
+    pd = 1;
+  } else {
+    pd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0644);
+    if (pd < 0) {
+      r = -errno;
+      std::cerr << "rbd: error create " << path << std::endl;
+      goto done;
+    }
+  }
+
+  //We just handle the case like 'banner, [ftag], [ttag], stag, [wztag]*,etag',
+  // and the (offset,length) in wztag must be ascending order.
+
+  r = parse_diff_header(fd, &f_tag, &f_from, &f_to, &f_size);
+  if (r < 0) {
+    std::cerr << "rbd: failed to parse first diff header" << std::endl;
+    goto done;
+  }
+
+  r = parse_diff_header(sd, &s_tag, &s_from, &s_to, &s_size);
+  if (r < 0) {
+    std::cerr << "rbd: failed to parse second diff header" << std::endl;
+    goto done;
+  }
+
+  if (f_to != s_from) {
+    r = -EINVAL;
+    std::cerr << "The first TO snapshot must be equal with the second FROM "
+              << "snapshot, aborting" << std::endl;
+    goto done;
+  }
+
+  {
+    // header
+    bufferlist bl;
+    bl.append(utils::RBD_DIFF_BANNER);
+
+    __u8 tag;
+    if (f_from.size()) {
+      tag = 'f';
+      ::encode(tag, bl);
+      ::encode(f_from, bl);
+    }
+
+    if (s_to.size()) {
+      tag = 't';
+      ::encode(tag, bl);
+      ::encode(s_to, bl);
+    }
+
+    tag = 's';
+    ::encode(tag, bl);
+    ::encode(s_size, bl);
+
+    r = bl.write_fd(pd);
+    if (r < 0) {
+      std::cerr << "rbd: failed to write merged diff header" << std::endl;
+      goto done;
+    }
+  }
+  if (f_size > s_size)
+    pc_size = f_size << 1;
+  else
+    pc_size = s_size << 1;
+
+  //data block
+  while (!f_end || !s_end) {
+    // progress through input
+    pc.update_progress(f_off + s_off, pc_size);
+
+    if (!f_end && !f_len) {
+      uint64_t last_off = f_off;
+
+      r = parse_diff_body(fd, &f_tag, &f_off, &f_len);
+      dout(2) << "first diff data chunk: tag=" << f_tag << ", "
+              << "off=" << f_off << ", "
+              << "len=" << f_len << dendl;
+      if (r < 0) {
+        std::cerr << "rbd: failed to read first diff data chunk header"
+                  << std::endl;
+        goto done;
+      }
+
+      if (f_tag == 'e') {
+        f_end = true;
+        f_tag = 'z';
+        f_off = f_size;
+        if (f_size < s_size)
+          f_len = s_size - f_size;
+        else
+          f_len = 0;
+      }
+
+      if (last_off > f_off) {
+        r = -ENOTSUP;
+        std::cerr << "rbd: out-of-order offset from first diff ("
+             << last_off << " > " << f_off << ")" << std::endl;
+        goto done;
+      }
+    }
+
+    if (!s_end && !s_len) {
+      uint64_t last_off = s_off;
+
+      r = parse_diff_body(sd, &s_tag, &s_off, &s_len);
+      dout(2) << "second diff data chunk: tag=" << f_tag << ", "
+              << "off=" << f_off << ", "
+              << "len=" << f_len << dendl;
+      if (r < 0) {
+        std::cerr << "rbd: failed to read second diff data chunk header"
+                  << std::endl;
+        goto done;
+      }
+
+      if (s_tag == 'e') {
+        s_end = true;
+        s_off = s_size;
+        if (s_size < f_size)
+          s_len = f_size - s_size;
+        else
+          s_len = 0;
+      }
+
+      if (last_off > s_off) {
+        r = -ENOTSUP;
+        std::cerr << "rbd: out-of-order offset from second diff ("
+                  << last_off << " > " << s_off << ")" << std::endl;
+        goto done;
+      }
+    }
+
+    if (f_off < s_off && f_len) {
+      uint64_t delta = s_off - f_off;
+      if (delta > f_len)
+        delta = f_len;
+      r = accept_diff_body(fd, pd, f_tag, f_off, delta);
+      f_off += delta;
+      f_len -= delta;
+
+      if (!f_len) {
+        f_tag = 0;
+        continue;
+      }
+    }
+    assert(f_off >= s_off);
+
+    if (f_off < s_off + s_len && f_len) {
+      uint64_t delta = s_off + s_len - f_off;
+      if (delta > f_len)
+        delta = f_len;
+      if (f_tag == 'w') {
+        if (first_stdin) {
+          bufferptr bp = buffer::create(delta);
+          r = safe_read_exact(fd, bp.c_str(), delta);
+        } else {
+          r = lseek(fd, delta, SEEK_CUR);
+        }
+        if (r < 0) {
+          std::cerr << "rbd: failed to skip first diff data" << std::endl;
+          goto done;
+        }
+      }
+      f_off += delta;
+      f_len -= delta;
+
+      if (!f_len) {
+        f_tag = 0;
+        continue;
+      }
+    }
+    assert(f_off >= s_off + s_len);
+    if (s_len) {
+      r = accept_diff_body(sd, pd, s_tag, s_off, s_len);
+      s_off += s_len;
+      s_len = 0;
+      s_tag = 0;
+    } else
+      assert(f_end && s_end);
+    continue;
+  }
+
+  {//tail
+    __u8 tag = 'e';
+    bufferlist bl;
+    ::encode(tag, bl);
+    r = bl.write_fd(pd);
+  }
+
+done:
+  if (pd > 2)
+    close(pd);
+  if (sd > 2)
+    close(sd);
+  if (fd > 2)
+    close(fd);
+
+  if(r < 0) {
+    pc.fail();
+    if (pd > 2)
+      unlink(path);
+  } else
+    pc.finish();
+
+  return r;
+}
+
 void get_arguments(po::options_description *positional,
                    po::options_description *options) {
   positional->add_options()
@@ -47,6 +417,13 @@ int execute(const po::variables_map &vm) {
     return r;
   }
 
+  r = do_merge_diff(first_diff.c_str(), second_diff.c_str(), path.c_str(),
+                    vm[at::NO_PROGRESS].as<bool>());
+  if (r < 0) {
+    cerr << "rbd: merge-diff error" << std::endl;
+    return -r;
+  }
+
   return 0;
 }
 
index ab23dc6f8bdbe8ea646e48c329c12b72e84b781f..b14bc72d0ddf89d2f1fe820bd2bcd06004b35368 100644 (file)
@@ -15,6 +15,18 @@ namespace object_map {
 namespace at = argument_types;
 namespace po = boost::program_options;
 
+static int do_object_map_rebuild(librbd::Image &image, bool no_progress)
+{
+  utils::ProgressContext pc("Object Map Rebuild", no_progress);
+  int r = image.rebuild_object_map(pc);
+  if (r < 0) {
+    pc.fail();
+    return r;
+  }
+  pc.finish();
+  return 0;
+}
+
 void get_arguments(po::options_description *positional,
                    po::options_description *options) {
   at::add_image_or_snap_spec_options(positional, options,
@@ -43,6 +55,12 @@ int execute(const po::variables_map &vm) {
     return r;
   }
 
+  r = do_object_map_rebuild(image, vm[at::NO_PROGRESS].as<bool>());
+  if (r < 0) {
+    std::cerr << "rbd: rebuilding object map failed: " << cpp_strerror(r)
+              << std::endl;
+    return r;
+  }
   return 0;
 }
 
index f7244c92ed4663699c2c0efe0d6b806944b6849b..6c2d2c3e2cd317eb5823b24b38ad812abe0a220f 100644 (file)
@@ -15,6 +15,19 @@ namespace remove {
 namespace at = argument_types;
 namespace po = boost::program_options;
 
+static int do_delete(librbd::RBD &rbd, librados::IoCtx& io_ctx,
+                     const char *imgname, bool no_progress)
+{
+  utils::ProgressContext pc("Removing image", no_progress);
+  int r = rbd.remove_with_progress(io_ctx, imgname, pc);
+  if (r < 0) {
+    pc.fail();
+    return r;
+  }
+  pc.finish();
+  return 0;
+}
+
 void get_arguments(po::options_description *positional,
                    po::options_description *options) {
   at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
@@ -40,6 +53,26 @@ int execute(const po::variables_map &vm) {
     return r;
   }
 
+  librbd::RBD rbd;
+  r = do_delete(rbd, io_ctx, image_name.c_str(),
+                vm[at::NO_PROGRESS].as<bool>());
+  if (r < 0) {
+    if (r == -ENOTEMPTY) {
+      std::cerr << "rbd: image has snapshots - these must be deleted"
+                << " with 'rbd snap purge' before the image can be removed."
+                << std::endl;
+    } else if (r == -EBUSY) {
+      std::cerr << "rbd: error: image still has watchers"
+                << std::endl
+                << "This means the image is still open or the client using "
+                << "it crashed. Try again after closing/unmapping it or "
+                << "waiting 30s for the crashed client to timeout."
+                << std::endl;
+    } else {
+      std::cerr << "rbd: delete error: " << cpp_strerror(r) << std::endl;
+    }
+    return r ;
+  }
   return 0;
 }
 
index a600c5f03d9bbf68d9c7d366e309bfb13f88a19d..c0761118e8e36123a3b341407c471916c5e6423b 100644 (file)
@@ -15,6 +15,15 @@ namespace rename {
 namespace at = argument_types;
 namespace po = boost::program_options;
 
+static int do_rename(librbd::RBD &rbd, librados::IoCtx& io_ctx,
+                     const char *imgname, const char *destname)
+{
+  int r = rbd.rename(io_ctx, imgname, destname);
+  if (r < 0)
+    return r;
+  return 0;
+}
+
 void get_arguments(po::options_description *positional,
                    po::options_description *options) {
   at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_SOURCE);
@@ -57,6 +66,12 @@ int execute(const po::variables_map &vm) {
     return r;
   }
 
+  librbd::RBD rbd;
+  r = do_rename(rbd, io_ctx, image_name.c_str(), dst_image_name.c_str());
+  if (r < 0) {
+    std::cerr << "rbd: rename error: " << cpp_strerror(r) << std::endl;
+    return r;
+  }
   return 0;
 }
 
index a2ad87896f3f18f5bbce808f9c9f0be0ee34ef1c..aa7a3904b7b27477eddc333a4953185cb899d8e2 100644 (file)
@@ -15,6 +15,18 @@ namespace resize {
 namespace at = argument_types;
 namespace po = boost::program_options;
 
+static int do_resize(librbd::Image& image, uint64_t size, bool no_progress)
+{
+  utils::ProgressContext pc("Resizing image", no_progress);
+  int r = image.resize_with_progress(size, pc);
+  if (r < 0) {
+    pc.fail();
+    return r;
+  }
+  pc.finish();
+  return 0;
+}
+
 void get_arguments(po::options_description *positional,
                    po::options_description *options) {
   at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
@@ -64,6 +76,11 @@ int execute(const po::variables_map &vm) {
     return -EINVAL;
   }
 
+  r = do_resize(image, size, vm[at::NO_PROGRESS].as<bool>());
+  if (r < 0) {
+    std::cerr << "rbd: resize error: " << cpp_strerror(r) << std::endl;
+    return r;
+  }
   return 0;
 }
 
index a73a693cd4529f555e677235afba8e393a4f25b2..a94060f1bfef9304c5f1ee3126309acc1989d9c6 100644 (file)
@@ -4,8 +4,11 @@
 #include "tools/rbd/ArgumentTypes.h"
 #include "tools/rbd/Shell.h"
 #include "tools/rbd/Utils.h"
+#include "include/types.h"
+#include "include/stringify.h"
 #include "common/errno.h"
 #include "common/Formatter.h"
+#include "common/TextTable.h"
 #include <iostream>
 #include <boost/program_options.hpp>
 
@@ -16,6 +19,135 @@ namespace snap {
 namespace at = argument_types;
 namespace po = boost::program_options;
 
+int do_list_snaps(librbd::Image& image, Formatter *f)
+{
+  std::vector<librbd::snap_info_t> snaps;
+  TextTable t;
+  int r;
+
+  r = image.snap_list(snaps);
+  if (r < 0)
+    return r;
+
+  if (f) {
+    f->open_array_section("snapshots");
+  } else {
+    t.define_column("SNAPID", TextTable::RIGHT, TextTable::RIGHT);
+    t.define_column("NAME", TextTable::LEFT, TextTable::LEFT);
+    t.define_column("SIZE", TextTable::RIGHT, TextTable::RIGHT);
+  }
+
+  for (std::vector<librbd::snap_info_t>::iterator s = snaps.begin();
+       s != snaps.end(); ++s) {
+    if (f) {
+      f->open_object_section("snapshot");
+      f->dump_unsigned("id", s->id);
+      f->dump_string("name", s->name);
+      f->dump_unsigned("size", s->size);
+      f->close_section();
+    } else {
+      t << s->id << s->name << stringify(prettybyte_t(s->size))
+        << TextTable::endrow;
+    }
+  }
+
+  if (f) {
+    f->close_section();
+    f->flush(std::cout);
+  } else if (snaps.size()) {
+    std::cout << t;
+  }
+
+  return 0;
+}
+
+int do_add_snap(librbd::Image& image, const char *snapname)
+{
+  int r = image.snap_create(snapname);
+  if (r < 0)
+    return r;
+
+  return 0;
+}
+
+int do_remove_snap(librbd::Image& image, const char *snapname)
+{
+  int r = image.snap_remove(snapname);
+  if (r < 0)
+    return r;
+
+  return 0;
+}
+
+int do_rollback_snap(librbd::Image& image, const char *snapname,
+                     bool no_progress)
+{
+  utils::ProgressContext pc("Rolling back to snapshot", no_progress);
+  int r = image.snap_rollback_with_progress(snapname, pc);
+  if (r < 0) {
+    pc.fail();
+    return r;
+  }
+  pc.finish();
+  return 0;
+}
+
+int do_purge_snaps(librbd::Image& image, bool no_progress)
+{
+  utils::ProgressContext pc("Removing all snapshots", no_progress);
+  std::vector<librbd::snap_info_t> snaps;
+  bool is_protected = false;
+  int r = image.snap_list(snaps);
+  if (r < 0) {
+    pc.fail();
+    return r;
+  } else if (0 == snaps.size()) {
+    return 0;
+  } else {
+    for (size_t i = 0; i < snaps.size(); ++i) {
+      r = image.snap_is_protected(snaps[i].name.c_str(), &is_protected);
+      if (r < 0) {
+        pc.fail();
+        return r;
+      } else if (is_protected == true) {
+        pc.fail();
+        std::cerr << "\r" << "rbd: snapshot '" << snaps[i].name.c_str()
+                  << "' is protected from removal." << std::endl;
+        return -EBUSY;
+      }
+    }
+    for (size_t i = 0; i < snaps.size(); ++i) {
+      r = image.snap_remove(snaps[i].name.c_str());
+      if (r < 0) {
+        pc.fail();
+        return r;
+      }
+      pc.update_progress(i + 1, snaps.size());
+    }
+
+    pc.finish();
+    return 0;
+  }
+}
+
+int do_protect_snap(librbd::Image& image, const char *snapname)
+{
+  int r = image.snap_protect(snapname);
+  if (r < 0)
+    return r;
+
+  return 0;
+}
+
+int do_unprotect_snap(librbd::Image& image, const char *snapname)
+{
+  int r = image.snap_unprotect(snapname);
+  if (r < 0)
+    return r;
+
+  return 0;
+}
+
 void get_list_arguments(po::options_description *positional,
                         po::options_description *options) {
   at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
@@ -49,6 +181,12 @@ int execute_list(const po::variables_map &vm) {
     return r;
   }
 
+  r = do_list_snaps(image, formatter.get());
+  if (r < 0) {
+    cerr << "rbd: failed to list snapshots: " << cpp_strerror(r)
+         << std::endl;
+    return r;
+  }
   return 0;
 }
 
@@ -78,6 +216,12 @@ int execute_create(const po::variables_map &vm) {
     return r;
   }
 
+  r = do_add_snap(image, snap_name.c_str());
+  if (r < 0) {
+    cerr << "rbd: failed to create snapshot: " << cpp_strerror(r)
+         << std::endl;
+    return r;
+  }
   return 0;
 }
 
@@ -107,6 +251,17 @@ int execute_remove(const po::variables_map &vm) {
     return r;
   }
 
+  r = do_remove_snap(image, snap_name.c_str());
+  if (r < 0) {
+    if (r == -EBUSY) {
+      std::cerr << "rbd: snapshot '" << snap_name << "' "
+                << "is protected from removal." << std::endl;
+    } else {
+      std::cerr << "rbd: failed to remove snapshot: " << cpp_strerror(r)
+                << std::endl;
+    }
+    return r;
+  }
   return 0;
 }
 
@@ -137,6 +292,14 @@ int execute_purge(const po::variables_map &vm) {
     return r;
   }
 
+  r = do_purge_snaps(image, vm[at::NO_PROGRESS].as<bool>());
+  if (r < 0) {
+    if (r != -EBUSY) {
+      std::cerr << "rbd: removing snaps failed: " << cpp_strerror(r)
+                << std::endl;
+    }
+    return r;
+  }
   return 0;
 }
 
@@ -167,6 +330,12 @@ int execute_rollback(const po::variables_map &vm) {
     return r;
   }
 
+  r = do_rollback_snap(image, snap_name.c_str(),
+                       vm[at::NO_PROGRESS].as<bool>());
+  if (r < 0) {
+    std::cerr << "rbd: rollback failed: " << cpp_strerror(r) << std::endl;
+    return r;
+  }
   return 0;
 }
 
@@ -196,6 +365,12 @@ int execute_protect(const po::variables_map &vm) {
     return r;
   }
 
+  r = do_protect_snap(image, snap_name.c_str());
+  if (r < 0) {
+    std::cerr << "rbd: protecting snap failed: " << cpp_strerror(r)
+              << std::endl;
+    return r;
+  }
   return 0;
 }
 
@@ -225,6 +400,12 @@ int execute_unprotect(const po::variables_map &vm) {
     return r;
   }
 
+  r = do_unprotect_snap(image, snap_name.c_str());
+  if (r < 0) {
+    std::cerr << "rbd: unprotecting snap failed: " << cpp_strerror(r)
+              << std::endl;
+    return r;
+  }
   return 0;
 }
 
index 5cf4b72f5eff58df870d9d65a8da880a3f3f5a8e..da8fe97e2cb57707f868b715b9a783e042fd4605 100644 (file)
@@ -4,6 +4,7 @@
 #include "tools/rbd/ArgumentTypes.h"
 #include "tools/rbd/Shell.h"
 #include "tools/rbd/Utils.h"
+#include "include/rbd_types.h"
 #include "common/errno.h"
 #include "common/Formatter.h"
 #include <iostream>
@@ -16,6 +17,72 @@ namespace status {
 namespace at = argument_types;
 namespace po = boost::program_options;
 
+static int do_show_status(librados::IoCtx &io_ctx, librbd::Image &image,
+                          const char *imgname, Formatter *f)
+{
+  librbd::image_info_t info;
+  uint8_t old_format;
+  int r;
+  std::string header_oid;
+  std::list<obj_watch_t> watchers;
+
+  r = image.old_format(&old_format);
+  if (r < 0)
+    return r;
+
+  if (old_format) {
+    header_oid = imgname;
+    header_oid += RBD_SUFFIX;
+  } else {
+    r = image.stat(info, sizeof(info));
+    if (r < 0)
+      return r;
+
+    char prefix[RBD_MAX_BLOCK_NAME_SIZE + 1];
+    strncpy(prefix, info.block_name_prefix, RBD_MAX_BLOCK_NAME_SIZE);
+    prefix[RBD_MAX_BLOCK_NAME_SIZE] = '\0';
+
+    header_oid = RBD_HEADER_PREFIX;
+    header_oid.append(prefix + strlen(RBD_DATA_PREFIX));
+  }
+
+  r = io_ctx.list_watchers(header_oid, &watchers);
+  if (r < 0)
+    return r;
+
+  if (f)
+    f->open_object_section("status");
+
+  if (f) {
+    f->open_object_section("watchers");
+    for (std::list<obj_watch_t>::iterator i = watchers.begin(); i != watchers.end(); ++i) {
+      f->open_object_section("watcher");
+      f->dump_string("address", i->addr);
+      f->dump_unsigned("client", i->watcher_id);
+      f->dump_unsigned("cookie", i->cookie);
+      f->close_section();
+    }
+    f->close_section();
+  } else {
+    if (watchers.size()) {
+      std::cout << "Watchers:" << std::endl;
+      for (std::list<obj_watch_t>::iterator i = watchers.begin();
+           i != watchers.end(); ++i) {
+        std::cout << "\twatcher=" << i->addr << " client." << i->watcher_id
+                  << " cookie=" << i->cookie << std::endl;
+      }
+    } else {
+      std::cout << "Watchers: none" << std::endl;
+    }
+  }
+  if (f) {
+    f->close_section();
+    f->flush(std::cout);
+  }
+
+  return 0;
+}
+
 void get_arguments(po::options_description *positional,
                    po::options_description *options) {
   at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
@@ -49,6 +116,11 @@ int execute(const po::variables_map &vm) {
     return r;
   }
 
+  r = do_show_status(io_ctx, image, image_name.c_str(), formatter.get());
+  if (r < 0) {
+    std::cerr << "rbd: show status failed: " << cpp_strerror(r) << std::endl;
+    return r;
+  }
   return 0;
 }
 
index 1570ca8d146926fcdf2246e3dc523d3c7c976113..3e532550d93099b32819f6213b4b538eccb57c83 100644 (file)
@@ -4,6 +4,7 @@
 #include "tools/rbd/ArgumentTypes.h"
 #include "tools/rbd/Shell.h"
 #include "tools/rbd/Utils.h"
+#include "include/rbd_types.h"
 #include "common/errno.h"
 #include <iostream>
 #include <boost/program_options.hpp>
@@ -46,6 +47,54 @@ private:
   std::string m_header_oid;
 };
 
+static int do_watch(librados::IoCtx& pp, librbd::Image &image,
+                    const char *imgname)
+{
+  uint8_t old_format;
+  int r = image.old_format(&old_format);
+  if (r < 0) {
+    std::cerr << "failed to query format" << std::endl;
+    return r;
+  }
+
+  std::string header_oid;
+  if (old_format != 0) {
+    header_oid = std::string(imgname) + RBD_SUFFIX;
+  } else {
+    librbd::image_info_t info;
+    r = image.stat(info, sizeof(info));
+    if (r < 0) {
+      std::cerr << "failed to stat image" << std::endl;
+      return r;
+    }
+
+    char prefix[RBD_MAX_BLOCK_NAME_SIZE + 1];
+    strncpy(prefix, info.block_name_prefix, RBD_MAX_BLOCK_NAME_SIZE);
+    prefix[RBD_MAX_BLOCK_NAME_SIZE] = '\0';
+
+    std::string image_id(prefix + strlen(RBD_DATA_PREFIX));
+    header_oid = RBD_HEADER_PREFIX + image_id;
+  }
+
+  uint64_t cookie;
+  RbdWatchCtx ctx(pp, imgname, header_oid);
+  r = pp.watch2(header_oid, &cookie, &ctx);
+  if (r < 0) {
+    std::cerr << "rbd: watch failed" << std::endl;
+    return r;
+  }
+
+  std::cout << "press enter to exit..." << std::endl;
+  getchar();
+
+  r = pp.unwatch2(cookie);
+  if (r < 0) {
+    std::cerr << "rbd: unwatch failed" << std::endl;
+    return r;
+  }
+  return 0;
+}
+
 void get_arguments(po::options_description *positional,
                    po::options_description *options) {
   at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
@@ -72,6 +121,11 @@ int execute(const po::variables_map &vm) {
     return r;
   }
 
+  r = do_watch(io_ctx, image, image_name.c_str());
+  if (r < 0) {
+    std::cerr << "rbd: watch failed: " << cpp_strerror(r) << std::endl;
+    return r;
+  }
   return 0;
 }