]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
rbd: stub versions of all existing CLI commands
authorJason Dillaman <dillaman@redhat.com>
Fri, 18 Sep 2015 20:40:21 +0000 (16:40 -0400)
committerJason Dillaman <dillaman@redhat.com>
Thu, 5 Nov 2015 21:11:53 +0000 (16:11 -0500)
All existing CLI command processing has been stubbed out using the
new self-registering command implementation.

Signed-off-by: Jason Dillaman <dillaman@redhat.com>
33 files changed:
src/CMakeLists.txt
src/tools/Makefile-client.am
src/tools/rbd/ArgumentTypes.cc [new file with mode: 0644]
src/tools/rbd/ArgumentTypes.h [new file with mode: 0644]
src/tools/rbd/Shell.cc
src/tools/rbd/Utils.cc [new file with mode: 0644]
src/tools/rbd/Utils.h [new file with mode: 0644]
src/tools/rbd/action/BenchWrite.cc [new file with mode: 0644]
src/tools/rbd/action/Children.cc [new file with mode: 0644]
src/tools/rbd/action/Clone.cc [new file with mode: 0644]
src/tools/rbd/action/Copy.cc [new file with mode: 0644]
src/tools/rbd/action/Create.cc [new file with mode: 0644]
src/tools/rbd/action/Diff.cc [new file with mode: 0644]
src/tools/rbd/action/DiskUsage.cc [new file with mode: 0644]
src/tools/rbd/action/Export.cc [new file with mode: 0644]
src/tools/rbd/action/ExportDiff.cc [new file with mode: 0644]
src/tools/rbd/action/Feature.cc [new file with mode: 0644]
src/tools/rbd/action/Flatten.cc [new file with mode: 0644]
src/tools/rbd/action/ImageMeta.cc [new file with mode: 0644]
src/tools/rbd/action/Import.cc [new file with mode: 0644]
src/tools/rbd/action/ImportDiff.cc [new file with mode: 0644]
src/tools/rbd/action/Info.cc [new file with mode: 0644]
src/tools/rbd/action/Kernel.cc [new file with mode: 0644]
src/tools/rbd/action/List.cc [new file with mode: 0644]
src/tools/rbd/action/Lock.cc [new file with mode: 0644]
src/tools/rbd/action/MergeDiff.cc [new file with mode: 0644]
src/tools/rbd/action/ObjectMap.cc [new file with mode: 0644]
src/tools/rbd/action/Remove.cc [new file with mode: 0644]
src/tools/rbd/action/Rename.cc [new file with mode: 0644]
src/tools/rbd/action/Resize.cc [new file with mode: 0644]
src/tools/rbd/action/Snap.cc [new file with mode: 0644]
src/tools/rbd/action/Status.cc [new file with mode: 0644]
src/tools/rbd/action/Watch.cc [new file with mode: 0644]

index dbba59626f11a80576c452d88b227cd9bd3ef1a5..66431be7bb2d536da6942aed374309528009366b 100644 (file)
@@ -876,15 +876,44 @@ if(${WITH_RBD})
   install(TARGETS librados librbd DESTINATION lib)
   set(rbd_srcs
     tools/rbd/rbd.cc
+    tools/rbd/ArgumentTypes.cc
     tools/rbd/IndentStream.cc
     tools/rbd/OptionPrinter.cc
     tools/rbd/Shell.cc
+    tools/rbd/Utils.cc
+    tools/rbd/action/BenchWrite.cc
+    tools/rbd/action/Children.cc
+    tools/rbd/action/Clone.cc
+    tools/rbd/action/Copy.cc
+    tools/rbd/action/Create.cc
+    tools/rbd/action/Diff.cc
+    tools/rbd/action/DiskUsage.cc
+    tools/rbd/action/Export.cc
+    tools/rbd/action/ExportDiff.cc
+    tools/rbd/action/Feature.cc
+    tools/rbd/action/Flatten.cc
+    tools/rbd/action/ImageMeta.cc
+    tools/rbd/action/Import.cc
+    tools/rbd/action/ImportDiff.cc
+    tools/rbd/action/Info.cc
+    tools/rbd/action/Kernel.cc
+    tools/rbd/action/List.cc
+    tools/rbd/action/Lock.cc
+    tools/rbd/action/MergeDiff.cc
+    tools/rbd/action/ObjectMap.cc
+    tools/rbd/action/Remove.cc
+    tools/rbd/action/Rename.cc
+    tools/rbd/action/Resize.cc
+    tools/rbd/action/Snap.cc
+    tools/rbd/action/Status.cc
+    tools/rbd/action/Watch.cc
     common/TextTable.cc)
   add_executable(rbd ${rbd_srcs} $<TARGET_OBJECTS:common_util_obj>
     $<TARGET_OBJECTS:parse_secret_objs>
     $<TARGET_OBJECTS:heap_profiler_objs>)
   set_target_properties(rbd PROPERTIES OUTPUT_NAME rbd)
   target_link_libraries(rbd librbd librados global common keyutils udev
+    boost_regex boost_program_options
     ${BLKID_LIBRARIES} ${CMAKE_DL_LIBS} ${TCMALLOC_LIBS})
   install(TARGETS rbd DESTINATION bin)
   install(PROGRAMS ${CMAKE_SOURCE_DIR}/src/ceph-rbdnamer DESTINATION bin)
index 6fd74da25133894d654f41cefad77815800c0aa0..1764eac05583b3cc460188048e348f99c9f5d3c1 100644 (file)
@@ -25,16 +25,46 @@ if WITH_RBD
 
 rbd_SOURCES = \
        tools/rbd/rbd.cc \
+       tools/rbd/ArgumentTypes.cc \
        tools/rbd/IndentStream.cc \
        tools/rbd/OptionPrinter.cc \
-       tools/rbd/Shell.cc
+       tools/rbd/Shell.cc \
+       tools/rbd/Utils.cc \
+       tools/rbd/action/BenchWrite.cc \
+       tools/rbd/action/Children.cc \
+       tools/rbd/action/Clone.cc \
+       tools/rbd/action/Copy.cc \
+       tools/rbd/action/Create.cc \
+       tools/rbd/action/Diff.cc \
+       tools/rbd/action/DiskUsage.cc \
+       tools/rbd/action/Export.cc \
+       tools/rbd/action/ExportDiff.cc \
+       tools/rbd/action/Feature.cc \
+       tools/rbd/action/Flatten.cc \
+       tools/rbd/action/ImageMeta.cc \
+       tools/rbd/action/Import.cc \
+       tools/rbd/action/ImportDiff.cc \
+       tools/rbd/action/Info.cc \
+       tools/rbd/action/Kernel.cc \
+       tools/rbd/action/List.cc \
+       tools/rbd/action/Lock.cc \
+       tools/rbd/action/MergeDiff.cc \
+       tools/rbd/action/ObjectMap.cc \
+       tools/rbd/action/Remove.cc \
+       tools/rbd/action/Rename.cc \
+       tools/rbd/action/Resize.cc \
+       tools/rbd/action/Snap.cc \
+       tools/rbd/action/Status.cc \
+       tools/rbd/action/Watch.cc
 noinst_HEADERS += \
+       tools/rbd/ArgumentTypes.h \
        tools/rbd/IndentStream.h \
        tools/rbd/OptionPrinter.h \
-       tools/rbd/Shell.h
+       tools/rbd/Shell.h \
+       tools/rbd/Utils.h
 rbd_LDADD = \
        $(LIBKRBD) $(LIBRBD) $(LIBRADOS) $(CEPH_GLOBAL) \
-       $(BOOST_PROGRAM_OPTIONS_LIBS)
+       $(BOOST_REGEX_LIBS) $(BOOST_PROGRAM_OPTIONS_LIBS)
 if LINUX
 bin_PROGRAMS += rbd
 endif # LINUX
diff --git a/src/tools/rbd/ArgumentTypes.cc b/src/tools/rbd/ArgumentTypes.cc
new file mode 100644 (file)
index 0000000..0cd6996
--- /dev/null
@@ -0,0 +1,337 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "tools/rbd/ArgumentTypes.h"
+#include "tools/rbd/Shell.h"
+#include "include/rbd/features.h"
+#include "common/config.h"
+#include "common/strtol.h"
+#include "common/Formatter.h"
+#include "global/global_context.h"
+#include <iostream>
+#include <boost/tokenizer.hpp>
+
+namespace rbd {
+namespace argument_types {
+
+namespace po = boost::program_options;
+
+const std::map<uint64_t, std::string> ImageFeatures::FEATURE_MAPPING = {
+  {RBD_FEATURE_LAYERING, "layering"},
+  {RBD_FEATURE_STRIPINGV2, "striping"},
+  {RBD_FEATURE_EXCLUSIVE_LOCK, "exclusive-lock"},
+  {RBD_FEATURE_OBJECT_MAP, "object-map"},
+  {RBD_FEATURE_FAST_DIFF, "fast-diff"},
+  {RBD_FEATURE_DEEP_FLATTEN, "deep-flatten"}};
+
+Format::Formatter Format::create_formatter(bool pretty) const {
+  if (value == "json") {
+    return Formatter(new JSONFormatter(pretty));
+  } else if (value == "xml") {
+    return Formatter(new XMLFormatter(pretty));
+  }
+  return Formatter();
+}
+
+std::string get_name_prefix(ArgumentModifier modifier) {
+  switch (modifier) {
+  case ARGUMENT_MODIFIER_SOURCE:
+    return SOURCE_PREFIX;
+  case ARGUMENT_MODIFIER_DEST:
+    return DEST_PREFIX;
+  default:
+    return "";
+  }
+}
+
+std::string get_description_prefix(ArgumentModifier modifier) {
+  switch (modifier) {
+  case ARGUMENT_MODIFIER_SOURCE:
+    return "source ";
+  case ARGUMENT_MODIFIER_DEST:
+    return "destination ";
+  default:
+    return "";
+  }
+}
+
+void add_pool_option(po::options_description *opt,
+                     ArgumentModifier modifier,
+                     const std::string &desc_suffix) {
+  std::string name = POOL_NAME + ",p";
+  std::string description = "pool name";
+  switch (modifier) {
+  case ARGUMENT_MODIFIER_NONE:
+    break;
+  case ARGUMENT_MODIFIER_SOURCE:
+    description = "source " + description;
+    break;
+  case ARGUMENT_MODIFIER_DEST:
+    name = DEST_POOL_NAME;
+    description = "destination " + description;
+    break;
+  }
+  description += desc_suffix;
+
+  // TODO add validator
+  opt->add_options()
+    (name.c_str(), po::value<std::string>(), description.c_str());
+}
+
+void add_image_option(po::options_description *opt,
+                      ArgumentModifier modifier,
+                      const std::string &desc_suffix) {
+  std::string name = IMAGE_NAME;
+  std::string description = "image name";
+  switch (modifier) {
+  case ARGUMENT_MODIFIER_NONE:
+    break;
+  case ARGUMENT_MODIFIER_SOURCE:
+    description = "source " + description;
+    break;
+  case ARGUMENT_MODIFIER_DEST:
+    name = DEST_IMAGE_NAME;
+    description = "destination " + description;
+    break;
+  }
+  description += desc_suffix;
+
+  // TODO add validator
+  opt->add_options()
+    (name.c_str(), po::value<std::string>(), description.c_str());
+}
+
+void add_snap_option(po::options_description *opt,
+                      ArgumentModifier modifier) {
+  std::string name = SNAPSHOT_NAME;
+  std::string description = "snapshot name";
+  switch (modifier) {
+  case ARGUMENT_MODIFIER_NONE:
+  case ARGUMENT_MODIFIER_DEST:
+    break;
+  case ARGUMENT_MODIFIER_SOURCE:
+    description = "source " + description;
+    break;
+  }
+
+  // TODO add validator
+  opt->add_options()
+    (name.c_str(), po::value<std::string>(), description.c_str());
+}
+
+void add_image_spec_options(po::options_description *pos,
+                            po::options_description *opt,
+                            ArgumentModifier modifier) {
+  pos->add_options()
+    ((get_name_prefix(modifier) + IMAGE_SPEC).c_str(),
+     (get_description_prefix(modifier) + "image specification\n" +
+      "(example: [<pool-name>/]<image-name>)").c_str());
+  add_pool_option(opt, modifier);
+  add_image_option(opt, modifier);
+}
+
+void add_snap_spec_options(po::options_description *pos,
+                           po::options_description *opt,
+                           ArgumentModifier modifier) {
+  pos->add_options()
+    ((get_name_prefix(modifier) + SNAPSHOT_SPEC).c_str(),
+     (get_description_prefix(modifier) + "snapshot specification\n" +
+      "(example: [<pool-name>/]<image-name>@<snapshot-name>)").c_str());
+  add_pool_option(opt, modifier);
+  add_image_option(opt, modifier);
+  add_snap_option(opt, modifier);
+}
+
+void add_image_or_snap_spec_options(po::options_description *pos,
+                                    po::options_description *opt,
+                                    ArgumentModifier modifier) {
+  pos->add_options()
+    ((get_name_prefix(modifier) + IMAGE_OR_SNAPSHOT_SPEC).c_str(),
+     (get_description_prefix(modifier) + "image or snapshot specification\n" +
+      "(example: [<pool-name>/]<image-name>[@<snap-name>])").c_str());
+  add_pool_option(opt, modifier);
+  add_image_option(opt, modifier);
+  add_snap_option(opt, modifier);
+}
+
+void add_create_image_options(po::options_description *opt,
+                              bool include_format) {
+  // TODO get default image format from conf
+  if (include_format) {
+    opt->add_options()
+      (IMAGE_FORMAT.c_str(), po::value<ImageFormat>(), "image format [1 or 2]")
+      (IMAGE_NEW_FORMAT.c_str(),
+       po::value<ImageNewFormat>()->zero_tokens(),
+       "use image format 2\n(deprecated)");
+  }
+
+  opt->add_options()
+    (IMAGE_ORDER.c_str(), po::value<ImageOrder>(),
+     "object order [12 <= order <= 25]")
+    (IMAGE_FEATURES.c_str(), po::value<ImageFeatures>()->multitoken(),
+     ("image features\n" + get_short_features_help(true)).c_str())
+    (IMAGE_SHARED.c_str(), po::bool_switch(), "shared image")
+    (IMAGE_STRIPE_UNIT.c_str(), po::value<uint32_t>(), "stripe unit")
+    (IMAGE_STRIPE_COUNT.c_str(), po::value<uint32_t>(), "stripe count");
+}
+
+void add_size_option(boost::program_options::options_description *opt) {
+  opt->add_options()
+    ((IMAGE_SIZE + ",s").c_str(), po::value<ImageSize>()->required(),
+     "image size (in M/G/T)");
+}
+
+void add_path_options(boost::program_options::options_description *pos,
+                      boost::program_options::options_description *opt,
+                      const std::string &description) {
+  pos->add_options()
+    (PATH_NAME.c_str(), po::value<std::string>(), description.c_str());
+  opt->add_options()
+    (PATH.c_str(), po::value<std::string>(), description.c_str());
+}
+
+void add_no_progress_option(boost::program_options::options_description *opt) {
+  opt->add_options()
+    (NO_PROGRESS.c_str(), po::bool_switch(), "disable progress output");
+}
+
+void add_format_options(boost::program_options::options_description *opt) {
+  opt->add_options()
+    (FORMAT.c_str(), po::value<Format>(), "output format [plain, json, or xml]")
+    (PRETTY_FORMAT.c_str(), po::bool_switch(),
+     "pretty formatting (json and xml)");
+}
+
+std::string get_short_features_help(bool append_suffix) {
+  std::ostringstream oss;
+  bool first_feature = true;
+  oss << "[";
+  for (auto &pair : ImageFeatures::FEATURE_MAPPING) {
+    if (!first_feature) {
+      oss << ", ";
+    }
+    first_feature = false;
+
+    std::string suffix;
+    if (append_suffix) {
+      if ((pair.first & RBD_FEATURES_MUTABLE) != 0) {
+        suffix += "*";
+      }
+      if ((pair.first & g_conf->rbd_default_features) != 0) {
+        suffix += "+";
+      }
+      if (!suffix.empty()) {
+        suffix = "(" + suffix + ")";
+      }
+    }
+    oss << pair.second << suffix;
+  }
+  oss << "]";
+  return oss.str();
+}
+
+std::string get_long_features_help() {
+  std::ostringstream oss;
+  oss << "Image Features:" << std::endl
+      << "  (*) supports enabling/disabling on existing images" << std::endl
+      << "  (+) enabled by default for new images if features not specified"
+      << std::endl;
+  return oss.str();
+}
+
+void validate(boost::any& v, const std::vector<std::string>& values,
+              ImageSize *target_type, int) {
+  po::validators::check_first_occurrence(v);
+  const std::string &s = po::validators::get_single_string(values);
+
+  std::string parse_error;
+  uint64_t size = strict_sistrtoll(s.c_str(), &parse_error);
+  if (!parse_error.empty()) {
+    throw po::validation_error(po::validation_error::invalid_option_value);
+  }
+
+  //NOTE: We can remove below given three lines of code once all applications,
+  //which use this CLI will adopt B/K/M/G/T/P/E with size value
+  if (isdigit(*s.rbegin())) {
+    size = size << 20;   // Default MB to Bytes
+  }
+  v = boost::any(size);
+}
+
+void validate(boost::any& v, const std::vector<std::string>& values,
+              ImageOrder *target_type, int dummy) {
+  po::validators::check_first_occurrence(v);
+  const std::string &s = po::validators::get_single_string(values);
+  try {
+    uint32_t order = boost::lexical_cast<uint32_t>(s);
+    if (order >= 12 && order <= 25) {
+      v = boost::any(order);
+      return;
+    }
+  } catch (const boost::bad_lexical_cast &) {
+  }
+  throw po::validation_error(po::validation_error::invalid_option_value);
+}
+
+void validate(boost::any& v, const std::vector<std::string>& values,
+              ImageFormat *target_type, int dummy) {
+  po::validators::check_first_occurrence(v);
+  const std::string &s = po::validators::get_single_string(values);
+  try {
+    uint32_t format = boost::lexical_cast<uint32_t>(s);
+    if (format == 1 || format == 2) {
+      v = boost::any(format);
+      return;
+    }
+  } catch (const boost::bad_lexical_cast &) {
+  }
+  throw po::validation_error(po::validation_error::invalid_option_value);
+}
+
+void validate(boost::any& v, const std::vector<std::string>& values,
+              ImageNewFormat *target_type, int dummy) {
+  std::cout << "rbd: --new-format is deprecated, use --image-format"
+            << std::endl;
+  v = boost::any(true);
+}
+
+void validate(boost::any& v, const std::vector<std::string>& values,
+              ImageFeatures *target_type, int) {
+  if (v.empty()) {
+    v = boost::any(static_cast<uint64_t>(0));
+  }
+
+  uint64_t &features = boost::any_cast<uint64_t &>(v);
+  for (auto &value : values) {
+    boost::char_separator<char> sep(",");
+    boost::tokenizer<boost::char_separator<char> > tok(value, sep);
+    for (auto &token : tok) {
+      bool matched = false;
+      for (auto &it : ImageFeatures::FEATURE_MAPPING) {
+        if (token == it.second) {
+          features |= it.first;
+          matched = true;
+          break;
+        }
+      }
+
+      if (!matched) {
+        throw po::validation_error(po::validation_error::invalid_option_value);
+      }
+    }
+  }
+}
+
+void validate(boost::any& v, const std::vector<std::string>& values,
+              Format *target_type, int) {
+  po::validators::check_first_occurrence(v);
+  const std::string &s = po::validators::get_single_string(values);
+  if (s == "plain" || s == "json" || s == "xml") {
+    v = boost::any(Format(s));
+  } else {
+    throw po::validation_error(po::validation_error::invalid_option_value);
+  }
+}
+
+} // namespace argument_types
+} // namespace rbd
diff --git a/src/tools/rbd/ArgumentTypes.h b/src/tools/rbd/ArgumentTypes.h
new file mode 100644 (file)
index 0000000..985279a
--- /dev/null
@@ -0,0 +1,157 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_RBD_ARGUMENT_TYPES_H
+#define CEPH_RBD_ARGUMENT_TYPES_H
+
+#include "include/int_types.h"
+#include <set>
+#include <string>
+#include <vector>
+#include <boost/any.hpp>
+#include <boost/program_options.hpp>
+#include <boost/shared_ptr.hpp>
+
+namespace ceph { class Formatter; }
+
+namespace rbd {
+namespace argument_types {
+
+enum ArgumentModifier {
+  ARGUMENT_MODIFIER_NONE,
+  ARGUMENT_MODIFIER_SOURCE,
+  ARGUMENT_MODIFIER_DEST
+};
+
+enum SpecFormat {
+  SPEC_FORMAT_IMAGE,
+  SPEC_FORMAT_SNAPSHOT,
+  SPEC_FORMAT_IMAGE_OR_SNAPSHOT
+};
+
+static const std::string DEFAULT_POOL_NAME("rbd");
+
+static const std::string SOURCE_PREFIX("source-");
+static const std::string DEST_PREFIX("dest-");
+
+// positional arguments
+static const std::string POSITIONAL_COMMAND_SPEC("positional-command-spec");
+static const std::string POSITIONAL_ARGUMENTS("positional-arguments");
+static const std::string IMAGE_SPEC("image-spec");
+static const std::string SNAPSHOT_SPEC("snap-spec");
+static const std::string IMAGE_OR_SNAPSHOT_SPEC("image-or-snap-spec");
+static const std::string PATH_NAME("path-name");
+
+// optional arguments
+static const std::string POOL_NAME("pool");
+static const std::string DEST_POOL_NAME("dest-pool");
+static const std::string IMAGE_NAME("image");
+static const std::string DEST_IMAGE_NAME("dest");
+static const std::string SNAPSHOT_NAME("snap");
+static const std::string PATH("path");
+static const std::string FROM_SNAPSHOT_NAME("from-snap");
+static const std::string WHOLE_OBJECT("whole-object");
+
+static const std::string IMAGE_FORMAT("image-format");
+static const std::string IMAGE_NEW_FORMAT("new-format");
+static const std::string IMAGE_ORDER("order");
+static const std::string IMAGE_FEATURES("image-features");
+static const std::string IMAGE_SHARED("image-shared");
+static const std::string IMAGE_SIZE("size");
+static const std::string IMAGE_STRIPE_UNIT("stripe-unit");
+static const std::string IMAGE_STRIPE_COUNT("stripe-count");
+
+static const std::string NO_PROGRESS("no-progress");
+static const std::string FORMAT("format");
+static const std::string PRETTY_FORMAT("pretty-format");
+
+static const std::set<std::string> SWITCH_ARGUMENTS = {
+  WHOLE_OBJECT, NO_PROGRESS, PRETTY_FORMAT};
+
+struct ImageSize {};
+struct ImageOrder {};
+struct ImageFormat {};
+struct ImageNewFormat {};
+
+struct ImageFeatures {
+  static const std::map<uint64_t, std::string>  FEATURE_MAPPING;
+
+  uint64_t features;
+};
+
+template <typename T>
+struct TypedValue {
+  T value;
+  TypedValue(const T& t) : value(t) {}
+};
+
+struct Format : public TypedValue<std::string> {
+  typedef boost::shared_ptr<ceph::Formatter> Formatter;
+
+  Format(const std::string &format) : TypedValue<std::string>(format) {}
+
+  Formatter create_formatter(bool pretty) const;
+};
+
+std::string get_name_prefix(ArgumentModifier modifier);
+std::string get_description_prefix(ArgumentModifier modifier);
+
+void add_pool_option(boost::program_options::options_description *opt,
+                     ArgumentModifier modifier,
+                     const std::string &desc_suffix = "");
+
+void add_image_option(boost::program_options::options_description *opt,
+                      ArgumentModifier modifier,
+                      const std::string &desc_suffix = "");
+
+void add_snap_option(boost::program_options::options_description *opt,
+                     ArgumentModifier modifier);
+
+void add_image_spec_options(boost::program_options::options_description *pos,
+                            boost::program_options::options_description *opt,
+                            ArgumentModifier modifier);
+
+void add_snap_spec_options(boost::program_options::options_description *pos,
+                           boost::program_options::options_description *opt,
+                           ArgumentModifier modifier);
+
+void add_image_or_snap_spec_options(
+  boost::program_options::options_description *pos,
+  boost::program_options::options_description *opt,
+  ArgumentModifier modifier);
+
+void add_create_image_options(boost::program_options::options_description *opt,
+                              bool include_format);
+
+void add_size_option(boost::program_options::options_description *opt);
+
+void add_path_options(boost::program_options::options_description *pos,
+                      boost::program_options::options_description *opt,
+                      const std::string &description);
+
+void add_no_progress_option(boost::program_options::options_description *opt);
+
+void add_format_options(boost::program_options::options_description *opt);
+
+std::string get_short_features_help(bool append_suffix);
+std::string get_long_features_help();
+
+void validate(boost::any& v, const std::vector<std::string>& values,
+              ImageSize *target_type, int);
+void validate(boost::any& v, const std::vector<std::string>& values,
+              ImageOrder *target_type, int);
+void validate(boost::any& v, const std::vector<std::string>& values,
+              ImageFormat *target_type, int);
+void validate(boost::any& v, const std::vector<std::string>& values,
+              ImageNewFormat *target_type, int);
+void validate(boost::any& v, const std::vector<std::string>& values,
+              ImageFeatures *target_type, int);
+void validate(boost::any& v, const std::vector<std::string>& values,
+              Format *target_type, int);
+
+std::ostream &operator<<(std::ostream &os, const ImageFeatures &features);
+
+} // namespace argument_types
+} // namespace rbd
+
+#endif // CEPH_RBD_ARGUMENT_TYPES_H
index 7bb0a89d677e0ceffbaa2c243bbbac2bedece47d..1cfc9b4bbc6b086231a6c249f8fd31e657cda44b 100644 (file)
@@ -2,6 +2,7 @@
 // vim: ts=8 sw=2 smarttab
 
 #include "tools/rbd/Shell.h"
+#include "tools/rbd/ArgumentTypes.h"
 #include "tools/rbd/IndentStream.h"
 #include "tools/rbd/OptionPrinter.h"
 #include "common/config.h"
@@ -13,6 +14,7 @@
 
 namespace rbd {
 
+namespace at = argument_types;
 namespace po = boost::program_options;
 
 namespace {
@@ -92,16 +94,16 @@ int Shell::execute(int arg_count, const char **arg_values) {
     // its associated positional arguments
     po::options_description arguments;
     arguments.add_options()
-      ("positional-command-spec",
+      (at::POSITIONAL_COMMAND_SPEC.c_str(),
        po::value<std::vector<std::string> >()->required(), "")
-      ("positional-arguments",
+      (at::POSITIONAL_ARGUMENTS.c_str(),
        po::value<std::vector<std::string> >(), "");
 
     po::positional_options_description positional_options;
-    positional_options.add("positional-command-spec",
+    positional_options.add(at::POSITIONAL_COMMAND_SPEC.c_str(),
                            matching_spec->size());
     if (command_spec.size() > matching_spec->size()) {
-      positional_options.add("positional-arguments", -1);
+      positional_options.add(at::POSITIONAL_ARGUMENTS.c_str(), -1);
     }
 
     po::options_description global;
@@ -118,15 +120,17 @@ int Shell::execute(int arg_count, const char **arg_values) {
       .allow_unregistered()
       .run(), vm);
 
-    if (vm["positional-command-spec"].as<std::vector<std::string> >() !=
+    if (vm[at::POSITIONAL_COMMAND_SPEC].as<std::vector<std::string> >() !=
           *matching_spec) {
       std::cerr << "rbd: failed to parse command" << std::endl;
       return EXIT_FAILURE;
     }
 
-    po::notify(vm);
-
     int r = (*action->execute)(vm);
+    if (r == -EINVAL) {
+      std::cout << std::endl;
+      print_action_help(app_name, action);
+    }
     if (r != 0) {
       return std::abs(r);
     }
@@ -157,6 +161,7 @@ void Shell::get_command_spec(int arg_count, const char **arg_values,
           (arg[1] == '-' || s_switch_arguments.count(arg.substr(1, 1)) == 0) &&
           (arg[1] != '-' ||
              s_switch_arguments.count(arg.substr(2, std::string::npos)) == 0) &&
+          at::SWITCH_ARGUMENTS.count(arg.substr(2, std::string::npos)) == 0 &&
           arg.find('=') == std::string::npos) {
         ++i;
       }
@@ -279,6 +284,10 @@ void Shell::print_action_help(const std::string &app_name, Action *action) {
 
   std::cout << std::endl;
   option_printer.print_detailed(std::cout);
+
+  if (!action->help.empty()) {
+    std::cout << action->help << std::endl;
+  }
 }
 
 void Shell::print_unknown_action(const std::string &app_name,
diff --git a/src/tools/rbd/Utils.cc b/src/tools/rbd/Utils.cc
new file mode 100644 (file)
index 0000000..0b811ff
--- /dev/null
@@ -0,0 +1,430 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "tools/rbd/Utils.h"
+#include "include/assert.h"
+#include "include/Context.h"
+#include "include/encoding.h"
+#include "common/common_init.h"
+#include "include/stringify.h"
+#include "include/rbd/features.h"
+#include "common/config.h"
+#include "common/errno.h"
+#include "common/safe_io.h"
+#include "global/global_context.h"
+#include <iostream>
+#include <boost/regex.hpp>
+
+namespace rbd {
+namespace utils {
+
+namespace at = argument_types;
+namespace po = boost::program_options;
+
+int ProgressContext::update_progress(uint64_t offset, uint64_t total) {
+  if (progress) {
+    int pc = total ? (offset * 100ull / total) : 0;
+    if (pc != last_pc) {
+      cerr << "\r" << operation << ": "
+           << pc << "% complete...";
+      cerr.flush();
+      last_pc = pc;
+    }
+  }
+  return 0;
+}
+
+void ProgressContext::finish() {
+  if (progress) {
+    cerr << "\r" << operation << ": 100% complete...done." << std::endl;
+  }
+}
+
+void ProgressContext::fail() {
+  if (progress) {
+    cerr << "\r" << operation << ": " << last_pc << "% complete...failed."
+         << std::endl;
+  }
+}
+
+void aio_context_callback(librbd::completion_t completion, void *arg)
+{
+  librbd::RBD::AioCompletion *aio_completion =
+    reinterpret_cast<librbd::RBD::AioCompletion*>(completion);
+  Context *context = reinterpret_cast<Context *>(arg);
+  context->complete(aio_completion->get_return_value());
+  aio_completion->release();
+}
+
+int read_string(int fd, unsigned max, std::string *out) {
+  char buf[4];
+
+  int r = safe_read_exact(fd, buf, 4);
+  if (r < 0)
+    return r;
+
+  bufferlist bl;
+  bl.append(buf, 4);
+  bufferlist::iterator p = bl.begin();
+  uint32_t len;
+  ::decode(len, p);
+  if (len > max)
+    return -EINVAL;
+
+  char sbuf[len];
+  r = safe_read_exact(fd, sbuf, len);
+  if (r < 0)
+    return r;
+  out->assign(sbuf, len);
+  return len;
+}
+
+int extract_spec(const std::string &spec, std::string *pool_name,
+                 std::string *image_name, std::string *snap_name) {
+  boost::regex pattern("^(?:([^/@]+)/)?([^/@]+)(?:@([^/@]+))?$");
+  boost::smatch match;
+  if (!boost::regex_match(spec, match, pattern)) {
+    std::cerr << "rbd: invalid spec '" << spec << "'" << std::endl;
+    return -EINVAL;
+  }
+
+  if (pool_name != nullptr && match[1].matched) {
+    *pool_name = match[1];
+  }
+  if (image_name != nullptr) {
+    *image_name = match[2];
+  }
+  if (snap_name != nullptr && match[3].matched) {
+    *snap_name = match[3];
+  }
+  return 0;
+}
+
+std::string get_positional_argument(const po::variables_map &vm, size_t index) {
+  if (vm.count(at::POSITIONAL_ARGUMENTS) == 0) {
+    return "";
+  }
+
+  const std::vector<std::string> &args =
+    boost::any_cast<std::vector<std::string> >(
+      vm[at::POSITIONAL_ARGUMENTS].value());
+  if (index < args.size()) {
+    return args[index];
+  }
+  return "";
+}
+
+int get_pool_image_snapshot_names(const po::variables_map &vm,
+                                  at::ArgumentModifier mod,
+                                  size_t *spec_arg_index,
+                                  std::string *pool_name,
+                                  std::string *image_name,
+                                  std::string *snap_name,
+                                  SnapshotPresence snapshot_presence,
+                                  bool image_required) {
+  std::string pool_key = (mod == at::ARGUMENT_MODIFIER_DEST ?
+    at::DEST_POOL_NAME : at::POOL_NAME);
+  std::string image_key = (mod == at::ARGUMENT_MODIFIER_DEST ?
+    at::DEST_IMAGE_NAME : at::IMAGE_NAME);
+
+  if (vm.count(pool_key) && pool_name != nullptr) {
+    *pool_name = vm[pool_key].as<std::string>();
+  }
+  if (vm.count(image_key) && image_name != nullptr) {
+    *image_name = vm[image_key].as<std::string>();
+  }
+  if (vm.count(at::SNAPSHOT_NAME) && snap_name != nullptr &&
+      mod != at::ARGUMENT_MODIFIER_DEST) {
+    *snap_name = vm[at::SNAPSHOT_NAME].as<std::string>();
+  }
+
+  if (image_name != nullptr && !image_name->empty()) {
+    // despite the separate pool and snapshot name options,
+    // we can also specify them via the image option
+    std::string image_name_copy(*image_name);
+    extract_spec(image_name_copy, pool_name, image_name, snap_name);
+  }
+
+  int r;
+  if (image_name != nullptr && spec_arg_index != nullptr &&
+      image_name->empty()) {
+    std::string spec = get_positional_argument(vm, (*spec_arg_index)++);
+    if (!spec.empty()) {
+      r = extract_spec(spec, pool_name, image_name, snap_name);
+      if (r < 0) {
+        return r;
+      }
+    }
+  }
+
+  if (pool_name->empty()) {
+    *pool_name = at::DEFAULT_POOL_NAME;
+  }
+
+  if (image_name != nullptr && image_required && image_name->empty()) {
+    std::string prefix = at::get_description_prefix(mod);
+    std::cerr << "rbd: "
+              << (mod == at::ARGUMENT_MODIFIER_DEST ? prefix : std::string())
+              << "image name was not specified" << std::endl;
+    return -EINVAL;
+  }
+
+  if (snap_name != nullptr) {
+    r = validate_snapshot_name(mod, *snap_name, snapshot_presence);
+    if (r < 0) {
+      return r;
+    }
+  }
+  return 0;
+}
+
+int validate_snapshot_name(at::ArgumentModifier mod,
+                           const std::string &snap_name,
+                           SnapshotPresence snapshot_presence) {
+  std::string prefix = at::get_description_prefix(mod);
+  switch (snapshot_presence) {
+  case SNAPSHOT_PRESENCE_PERMITTED:
+    break;
+  case SNAPSHOT_PRESENCE_NONE:
+    if (!snap_name.empty()) {
+      std::cerr << "rbd: "
+                << (mod == at::ARGUMENT_MODIFIER_DEST ? prefix : std::string())
+                << "snapname specified for a command that doesn't use it"
+                << std::endl;
+      return -EINVAL;
+    }
+    break;
+  case SNAPSHOT_PRESENCE_REQUIRED:
+    if (snap_name.empty()) {
+      std::cerr << "rbd: "
+                << (mod == at::ARGUMENT_MODIFIER_DEST ? prefix : std::string())
+                << "snap name was not specified" << std::endl;
+      return -EINVAL;
+    }
+    break;
+  }
+  return 0;
+}
+
+int get_image_options(const boost::program_options::variables_map &vm,
+                      int *order, uint32_t *format, uint64_t *features,
+                      uint32_t *stripe_unit, uint32_t *stripe_count) {
+  if (vm.count(at::IMAGE_ORDER)) {
+    *order = vm[at::IMAGE_ORDER].as<uint32_t>();
+  } else {
+    *order = 22;
+  }
+
+  bool features_specified = false;
+  if (vm.count(at::IMAGE_FEATURES)) {
+    *features = vm[at::IMAGE_FEATURES].as<uint64_t>();
+    features_specified = true;
+  } else {
+    *features = g_conf->rbd_default_features;
+  }
+
+  if (vm.count(at::IMAGE_STRIPE_UNIT)) {
+    *stripe_unit = vm[at::IMAGE_STRIPE_UNIT].as<uint32_t>();
+  } else {
+    *stripe_unit = g_conf->rbd_default_stripe_count;
+  }
+
+  if (vm.count(at::IMAGE_STRIPE_COUNT)) {
+    *stripe_count = vm[at::IMAGE_STRIPE_COUNT].as<uint32_t>();
+  } else {
+    *stripe_count = g_conf->rbd_default_stripe_unit;
+  }
+
+  if ((*stripe_unit != 0 && *stripe_count == 0) ||
+      (*stripe_unit == 0 && *stripe_count != 0)) {
+    std::cerr << "must specify both (or neither) of stripe-unit and stripe-count"
+              << std::endl;
+    return -EINVAL;
+  } else if ((*stripe_unit || *stripe_count) &&
+             (*stripe_unit != (1ll << *order) && *stripe_count != 1)) {
+    *features |= RBD_FEATURE_STRIPINGV2;
+  } else {
+    *features &= ~RBD_FEATURE_STRIPINGV2;
+  }
+
+  if (vm.count(at::IMAGE_SHARED) && vm[at::IMAGE_SHARED].as<bool>()) {
+    *features &= ~RBD_FEATURES_SINGLE_CLIENT;
+  }
+
+  if (format != nullptr) {
+    bool format_specified = false;
+    if (vm.count(at::IMAGE_NEW_FORMAT)) {
+      *format = 2;
+      format_specified = true;
+    } else if (vm.count(at::IMAGE_FORMAT)) {
+      *format = vm[at::IMAGE_FORMAT].as<uint32_t>();
+      format_specified = true;
+    } else {
+      *format = g_conf->rbd_default_format;
+    }
+
+    if (features_specified && *features != 0) {
+      if (format_specified && *format == 1) {
+        std::cerr << "rbd: features not allowed with format 1; "
+                  << "use --image-format 2" << std::endl;
+        return -EINVAL;
+      } else {
+        *format = 2;
+        format_specified = true;
+      }
+    }
+
+    if ((*stripe_unit || *stripe_count) &&
+        (*stripe_unit != (1ull << *order) && *stripe_count != 1)) {
+      if (format_specified && *format == 1) {
+        std::cerr << "rbd: non-default striping not allowed with format 1; "
+                  << "use --image-format 2" << std::endl;
+        return -EINVAL;
+      } else {
+        *format = 2;
+        format_specified = 2;
+      }
+    }
+
+    if (format_specified) {
+      int r = g_conf->set_val("rbd_default_format", stringify(*format));
+      assert(r == 0);
+    }
+  }
+
+  return 0;
+}
+
+int get_image_size(const boost::program_options::variables_map &vm,
+                   uint64_t *size) {
+  if (vm.count(at::IMAGE_SIZE) == 0) {
+    std::cerr << "rbd: must specify --size <M/G/T>" << std::endl;
+    return -EINVAL;
+  }
+
+  *size = vm[at::IMAGE_SIZE].as<uint64_t>();
+  return 0;
+}
+
+int get_path(const boost::program_options::variables_map &vm,
+             const std::string &positional_path, std::string *path) {
+  if (!positional_path.empty()) {
+    *path = positional_path;
+  } else if (vm.count(at::PATH)) {
+    *path = vm[at::PATH].as<std::string>();
+  }
+
+  if (path->empty()) {
+    std::cerr << "rbd: path was not specified" << std::endl;
+    return -EINVAL;
+  }
+  return 0;
+}
+
+int get_formatter(const po::variables_map &vm,
+                  at::Format::Formatter *formatter) {
+  if (vm.count(at::FORMAT)) {
+    bool pretty = vm[at::PRETTY_FORMAT].as<bool>();
+    *formatter = vm[at::FORMAT].as<at::Format>().create_formatter(pretty);
+    if (*formatter == nullptr && pretty) {
+      std::cerr << "rbd: --pretty-format only works when --format "
+                << "is json or xml" << std::endl;
+      return -EINVAL;
+    }
+  }
+  return 0;
+}
+
+void init_context() {
+  g_conf->set_val_or_die("rbd_cache_writethrough_until_flush", "false");
+  common_init_finish(g_ceph_context);
+}
+
+int init(const std::string &pool_name, librados::Rados *rados,
+         librados::IoCtx *io_ctx) {
+  init_context();
+
+  int r = rados->init_with_context(g_ceph_context);
+  if (r < 0) {
+    std::cerr << "rbd: couldn't initialize rados!" << std::endl;
+    return r;
+  }
+
+  r = rados->connect();
+  if (r < 0) {
+    std::cerr << "rbd: couldn't connect to the cluster!" << std::endl;
+    return r;
+  }
+
+  r = init_io_ctx(*rados, pool_name, io_ctx);
+  if (r < 0) {
+    return r;
+  }
+  return 0;
+}
+
+int init_io_ctx(librados::Rados &rados, const std::string &pool_name,
+                librados::IoCtx *io_ctx) {
+  int r = rados.ioctx_create(pool_name.c_str(), *io_ctx);
+  if (r < 0) {
+    std::cerr << "rbd: error opening pool " << pool_name << ": "
+              << cpp_strerror(r) << std::endl;
+    return r;
+  }
+  return 0;
+}
+
+int open_image(librados::IoCtx &io_ctx, const std::string &image_name,
+               bool read_only, librbd::Image *image) {
+  int r;
+  librbd::RBD rbd;
+  if (read_only) {
+    r = rbd.open_read_only(io_ctx, *image, image_name.c_str(), NULL);
+  } else {
+    r = rbd.open(io_ctx, *image, image_name.c_str());
+  }
+
+  if (r < 0) {
+    std::cerr << "rbd: error opening image " << image_name << ": "
+              << cpp_strerror(r) << std::endl;
+    return r;
+  }
+  return 0;
+}
+
+int init_and_open_image(const std::string &pool_name,
+                        const std::string &image_name,
+                        const std::string &snap_name, bool read_only,
+                        librados::Rados *rados, librados::IoCtx *io_ctx,
+                        librbd::Image *image) {
+  int r = init(pool_name, rados, io_ctx);
+  if (r < 0) {
+    return r;
+  }
+
+  r = open_image(*io_ctx, image_name, read_only, image);
+  if (r < 0) {
+    return r;
+  }
+
+  if (!snap_name.empty()) {
+    r = snap_set(*image, snap_name);
+    if (r < 0) {
+      return r;
+    }
+  }
+  return 0;
+}
+
+int snap_set(librbd::Image &image, const std::string snap_name) {
+  int r = image.snap_set(snap_name.c_str());
+  if (r < 0) {
+    std::cerr << "error setting snapshot context: " << cpp_strerror(r)
+              << std::endl;
+    return r;
+  }
+  return 0;
+}
+
+} // namespace utils
+} // namespace rbd
diff --git a/src/tools/rbd/Utils.h b/src/tools/rbd/Utils.h
new file mode 100644 (file)
index 0000000..0b7794e
--- /dev/null
@@ -0,0 +1,94 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_RBD_UTILS_H
+#define CEPH_RBD_UTILS_H
+
+#include "include/int_types.h"
+#include "include/rados/librados.hpp"
+#include "include/rbd/librbd.hpp"
+#include "tools/rbd/ArgumentTypes.h"
+#include <string>
+#include <boost/program_options.hpp>
+
+namespace rbd {
+namespace utils {
+
+static const std::string RBD_DIFF_BANNER ("rbd diff v1\n");
+
+enum SnapshotPresence {
+  SNAPSHOT_PRESENCE_NONE,
+  SNAPSHOT_PRESENCE_PERMITTED,
+  SNAPSHOT_PRESENCE_REQUIRED
+};
+
+struct ProgressContext : public librbd::ProgressContext {
+  const char *operation;
+  bool progress;
+  int last_pc;
+
+  ProgressContext(const char *o, bool no_progress)
+    : operation(o), progress(!no_progress), last_pc(0) {
+  }
+
+  int update_progress(uint64_t offset, uint64_t total);
+  void finish();
+  void fail();
+};
+
+void aio_context_callback(librbd::completion_t completion, void *arg);
+
+int read_string(int fd, unsigned max, std::string *out);
+
+int extract_spec(const std::string &spec, std::string *pool_name,
+                 std::string *image_name, std::string *snap_name);
+
+std::string get_positional_argument(
+    const boost::program_options::variables_map &vm, size_t index);
+
+int get_pool_image_snapshot_names(
+    const boost::program_options::variables_map &vm,
+    argument_types::ArgumentModifier mod, size_t *spec_arg_index,
+    std::string *pool_name, std::string *image_name, std::string *snap_name,
+    SnapshotPresence snapshot_presence, bool image_required = true);
+
+int validate_snapshot_name(argument_types::ArgumentModifier mod,
+                           const std::string &snap_name,
+                           SnapshotPresence snapshot_presence);
+
+int get_image_options(const boost::program_options::variables_map &vm,
+                      int *order, uint32_t *format, uint64_t *features,
+                      uint32_t *stripe_unit, uint32_t *stripe_count);
+
+int get_image_size(const boost::program_options::variables_map &vm,
+                   uint64_t *size);
+
+int get_path(const boost::program_options::variables_map &vm,
+             const std::string &positional_path, std::string *path);
+
+int get_formatter(const boost::program_options::variables_map &vm,
+                  argument_types::Format::Formatter *formatter);
+
+void init_context();
+
+int init(const std::string &pool_name, librados::Rados *rados,
+         librados::IoCtx *io_ctx);
+
+int init_io_ctx(librados::Rados &rados, const std::string &pool_name,
+                librados::IoCtx *io_ctx);
+
+int open_image(librados::IoCtx &io_ctx, const std::string &image_name,
+               bool read_only, librbd::Image *image);
+
+int init_and_open_image(const std::string &pool_name,
+                        const std::string &image_name,
+                        const std::string &snap_name, bool read_only,
+                        librados::Rados *rados, librados::IoCtx *io_ctx,
+                        librbd::Image *image);
+
+int snap_set(librbd::Image &image, const std::string snap_name);
+
+} // namespace utils
+} // namespace rbd
+
+#endif // CEPH_RBD_UTILS_H
diff --git a/src/tools/rbd/action/BenchWrite.cc b/src/tools/rbd/action/BenchWrite.cc
new file mode 100644 (file)
index 0000000..928ce54
--- /dev/null
@@ -0,0 +1,121 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "tools/rbd/ArgumentTypes.h"
+#include "tools/rbd/Shell.h"
+#include "tools/rbd/Utils.h"
+#include "common/errno.h"
+#include "common/strtol.h"
+#include <iostream>
+#include <boost/program_options.hpp>
+
+namespace rbd {
+namespace action {
+namespace bench_write {
+
+namespace at = argument_types;
+namespace po = boost::program_options;
+
+namespace {
+
+struct Size {};
+struct IOPattern {};
+
+void validate(boost::any& v, const std::vector<std::string>& values,
+              Size *target_type, int) {
+  po::validators::check_first_occurrence(v);
+  const std::string &s = po::validators::get_single_string(values);
+
+  std::string parse_error;
+  uint64_t size = strict_sistrtoll(s.c_str(), &parse_error);
+  if (!parse_error.empty()) {
+    throw po::validation_error(po::validation_error::invalid_option_value);
+  }
+  v = boost::any(size);
+}
+
+void validate(boost::any& v, const std::vector<std::string>& values,
+              IOPattern *target_type, int) {
+  po::validators::check_first_occurrence(v);
+  const std::string &s = po::validators::get_single_string(values);
+  if (s == "rand") {
+    v = boost::any(true);
+  } else if (s == "seq") {
+    v = boost::any(false);
+  } else {
+    throw po::validation_error(po::validation_error::invalid_option_value);
+  }
+
+}
+
+} // anonymous namespace
+
+void get_arguments(po::options_description *positional,
+                   po::options_description *options) {
+  at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
+  // TODO
+  options->add_options()
+    ("io-size", po::value<Size>(), "write size (in B/K/M/G/T)")
+    ("io-threads", po::value<uint32_t>(), "ios in flight")
+    ("io-total", po::value<Size>(), "total size to write (in B/K/M/G/T)")
+    ("io-pattern", po::value<IOPattern>(), "write pattern (rand or seq)");
+}
+
+int execute(const po::variables_map &vm) {
+  size_t arg_index = 0;
+  std::string pool_name;
+  std::string image_name;
+  std::string snap_name;
+  int r = utils::get_pool_image_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &image_name,
+    &snap_name, utils::SNAPSHOT_PRESENCE_NONE);
+  if (r < 0) {
+    return r;
+  }
+
+  uint64_t bench_io_size;
+  if (vm.count("io-size")) {
+    bench_io_size = vm["io-size"].as<uint64_t>();
+  } else {
+    bench_io_size = 4096;
+  }
+
+  uint32_t bench_io_threads;
+  if (vm.count("io-threads")) {
+    bench_io_threads = vm["io-threads"].as<uint32_t>();
+  } else {
+    bench_io_threads = 16;
+  }
+
+  uint64_t bench_bytes;
+  if (vm.count("io-total")) {
+    bench_bytes = vm["io-total"].as<uint64_t>();
+  } else {
+    bench_bytes = 1 << 30;
+  }
+
+  bool bench_random;
+  if (vm.count("io-pattern")) {
+    bench_random = vm["io-pattern"].as<bool>();
+  } else {
+    bench_random = false;
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  librbd::Image image;
+  r = utils::init_and_open_image(pool_name, image_name, "", false, &rados,
+                                 &io_ctx, &image);
+  if (r < 0) {
+    return r;
+  }
+
+  return 0;
+}
+
+Shell::Action action(
+  {"bench-write"}, {}, "Simple write benchmark.", "", &get_arguments, &execute);
+
+} // namespace bench_write
+} // namespace action
+} // namespace rbd
diff --git a/src/tools/rbd/action/Children.cc b/src/tools/rbd/action/Children.cc
new file mode 100644 (file)
index 0000000..f368a87
--- /dev/null
@@ -0,0 +1,61 @@
+// -*- mode:C++; tab-width:8; c-basic-offsset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "tools/rbd/ArgumentTypes.h"
+#include "tools/rbd/Shell.h"
+#include "tools/rbd/Utils.h"
+#include "common/errno.h"
+#include "common/Formatter.h"
+#include <iostream>
+#include <boost/program_options.hpp>
+
+namespace rbd {
+namespace action {
+namespace children {
+
+namespace at = argument_types;
+namespace po = boost::program_options;
+
+void get_arguments(po::options_description *positional,
+                   po::options_description *options) {
+  at::add_snap_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
+  at::add_format_options(options);
+}
+
+int execute(const po::variables_map &vm) {
+  size_t arg_index = 0;
+  std::string pool_name;
+  std::string image_name;
+  std::string snap_name;
+  int r = utils::get_pool_image_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &image_name,
+    &snap_name, utils::SNAPSHOT_PRESENCE_REQUIRED);
+  if (r < 0) {
+    return r;
+  }
+
+  at::Format::Formatter formatter;
+  r = utils::get_formatter(vm, &formatter);
+  if (r < 0) {
+    return r;
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  librbd::Image image;
+  r = utils::init_and_open_image(pool_name, image_name, snap_name, true,
+                                 &rados, &io_ctx, &image);
+  if (r < 0) {
+    return r;
+  }
+
+  return 0;
+}
+
+Shell::Action action(
+  {"children"}, {}, "Display children of snapshot.", "", &get_arguments,
+  &execute);
+
+} // namespace children
+} // namespace action
+} // namespace rbd
diff --git a/src/tools/rbd/action/Clone.cc b/src/tools/rbd/action/Clone.cc
new file mode 100644 (file)
index 0000000..08b6ddd
--- /dev/null
@@ -0,0 +1,79 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "tools/rbd/ArgumentTypes.h"
+#include "tools/rbd/Shell.h"
+#include "tools/rbd/Utils.h"
+#include "common/errno.h"
+#include <iostream>
+#include <boost/program_options.hpp>
+
+namespace rbd {
+namespace action {
+namespace clone {
+
+namespace at = argument_types;
+namespace po = boost::program_options;
+
+void get_arguments(po::options_description *positional,
+                   po::options_description *options) {
+  at::add_snap_spec_options(positional, options, at::ARGUMENT_MODIFIER_SOURCE);
+  at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_DEST);
+  at::add_create_image_options(options, false);
+}
+
+int execute(const po::variables_map &vm) {
+  size_t arg_index = 0;
+  std::string pool_name;
+  std::string image_name;
+  std::string snap_name;
+  int r = utils::get_pool_image_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_SOURCE, &arg_index, &pool_name, &image_name,
+    &snap_name, utils::SNAPSHOT_PRESENCE_REQUIRED);
+  if (r < 0) {
+    return r;
+  }
+
+  std::string dst_pool_name;
+  std::string dst_image_name;
+  std::string dst_snap_name;
+  r = utils::get_pool_image_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_DEST, &arg_index, &dst_pool_name, &dst_image_name,
+    &dst_snap_name, utils::SNAPSHOT_PRESENCE_NONE);
+  if (r < 0) {
+    return r;
+  }
+
+  int order;
+  uint64_t features;
+  uint32_t stripe_unit;
+  uint32_t stripe_count;
+  r = utils::get_image_options(vm, &order, nullptr, &features, &stripe_unit,
+                               &stripe_count);
+  if (r < 0) {
+    return r;
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  r = utils::init(pool_name, &rados, &io_ctx);
+  if (r < 0) {
+    return r;
+  }
+
+  librados::IoCtx dst_io_ctx;
+  r = utils::init_io_ctx(rados, dst_pool_name, &dst_io_ctx);
+  if (r < 0) {
+    return r;
+  }
+
+  return 0;
+}
+
+Shell::Action action(
+  {"clone"}, {}, "Clone a snapshot into a COW child image.",
+  at::get_long_features_help(), &get_arguments, &execute);
+
+} // namespace clone
+} // namespace action
+} // namespace rbd
diff --git a/src/tools/rbd/action/Copy.cc b/src/tools/rbd/action/Copy.cc
new file mode 100644 (file)
index 0000000..79369d9
--- /dev/null
@@ -0,0 +1,71 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "tools/rbd/ArgumentTypes.h"
+#include "tools/rbd/Shell.h"
+#include "tools/rbd/Utils.h"
+#include "common/errno.h"
+#include <iostream>
+#include <boost/program_options.hpp>
+
+namespace rbd {
+namespace action {
+namespace copy {
+
+namespace at = argument_types;
+namespace po = boost::program_options;
+
+void get_arguments(po::options_description *positional,
+                   po::options_description *options) {
+  at::add_image_or_snap_spec_options(positional, options,
+                                     at::ARGUMENT_MODIFIER_SOURCE);
+  at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_DEST);
+  at::add_no_progress_option(options);
+}
+
+int execute(const po::variables_map &vm) {
+  size_t arg_index = 0;
+  std::string pool_name;
+  std::string image_name;
+  std::string snap_name;
+  int r = utils::get_pool_image_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_SOURCE, &arg_index, &pool_name, &image_name,
+    &snap_name, utils::SNAPSHOT_PRESENCE_PERMITTED);
+  if (r < 0) {
+    return r;
+  }
+
+  std::string dst_pool_name;
+  std::string dst_image_name;
+  std::string dst_snap_name;
+  r = utils::get_pool_image_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_DEST, &arg_index, &dst_pool_name, &dst_image_name,
+    &dst_snap_name, utils::SNAPSHOT_PRESENCE_NONE);
+  if (r < 0) {
+    return r;
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  librbd::Image image;
+  r = utils::init_and_open_image(pool_name, image_name, snap_name, true,
+                                 &rados, &io_ctx, &image);
+  if (r < 0) {
+    return r;
+  }
+
+  librados::IoCtx dst_io_ctx;
+  r = utils::init_io_ctx(rados, dst_pool_name, &dst_io_ctx);
+  if (r < 0) {
+    return r;
+  }
+
+  return 0;
+}
+
+Shell::Action action(
+  {"copy"}, {"cp"}, "Copy src image to dest.", "", &get_arguments, &execute);
+
+} // namespace copy
+} // namespace action
+} // namespace rbd
diff --git a/src/tools/rbd/action/Create.cc b/src/tools/rbd/action/Create.cc
new file mode 100644 (file)
index 0000000..52285e2
--- /dev/null
@@ -0,0 +1,70 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "tools/rbd/ArgumentTypes.h"
+#include "tools/rbd/Shell.h"
+#include "tools/rbd/Utils.h"
+#include "common/errno.h"
+#include <iostream>
+#include <boost/program_options.hpp>
+
+namespace rbd {
+namespace action {
+namespace create {
+
+namespace at = argument_types;
+namespace po = boost::program_options;
+
+void get_arguments(po::options_description *positional,
+                   po::options_description *options) {
+  at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
+  at::add_create_image_options(options, true);
+  at::add_size_option(options);
+}
+
+int execute(const po::variables_map &vm) {
+  size_t arg_index = 0;
+  std::string pool_name;
+  std::string image_name;
+  std::string snap_name;
+  int r = utils::get_pool_image_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &image_name,
+    &snap_name, utils::SNAPSHOT_PRESENCE_NONE);
+  if (r < 0) {
+    return r;
+  }
+
+  int order;
+  uint32_t format;
+  uint64_t features;
+  uint32_t stripe_unit;
+  uint32_t stripe_count;
+  r = utils::get_image_options(vm, &order, &format, &features, &stripe_unit,
+                               &stripe_count);
+  if (r < 0) {
+    return r;
+  }
+
+  uint64_t size;
+  r = utils::get_image_size(vm, &size);
+  if (r < 0) {
+    return r;
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  r = utils::init(pool_name, &rados, &io_ctx);
+  if (r < 0) {
+    return r;
+  }
+
+  return 0;
+}
+
+Shell::Action action(
+  {"create"}, {}, "Create an empty image.", at::get_long_features_help(),
+  &get_arguments, &execute);
+
+} // namespace create
+} // namespace action
+} // namespace rbd
diff --git a/src/tools/rbd/action/Diff.cc b/src/tools/rbd/action/Diff.cc
new file mode 100644 (file)
index 0000000..921e296
--- /dev/null
@@ -0,0 +1,75 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "tools/rbd/ArgumentTypes.h"
+#include "tools/rbd/Shell.h"
+#include "tools/rbd/Utils.h"
+#include "common/errno.h"
+#include "common/Formatter.h"
+#include <iostream>
+#include <boost/program_options.hpp>
+
+namespace rbd {
+namespace action {
+namespace diff {
+
+namespace at = argument_types;
+namespace po = boost::program_options;
+
+void get_arguments(po::options_description *positional,
+                   po::options_description *options) {
+  at::add_image_or_snap_spec_options(positional, options,
+                                     at::ARGUMENT_MODIFIER_NONE);
+  options->add_options()
+    (at::FROM_SNAPSHOT_NAME.c_str(), po::value<std::string>(),
+     "snapshot starting point")
+    (at::WHOLE_OBJECT.c_str(), po::bool_switch(), "compare whole object");
+  at::add_format_options(options);
+}
+
+int execute(const po::variables_map &vm) {
+  size_t arg_index = 0;
+  std::string pool_name;
+  std::string image_name;
+  std::string snap_name;
+  int r = utils::get_pool_image_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &image_name,
+    &snap_name, utils::SNAPSHOT_PRESENCE_PERMITTED);
+  if (r < 0) {
+    return r;
+  }
+
+  std::string from_snap_name;
+  if (vm.count(at::FROM_SNAPSHOT_NAME)) {
+    from_snap_name = vm[at::FROM_SNAPSHOT_NAME].as<std::string>();
+  }
+
+  bool diff_whole_object = vm[at::WHOLE_OBJECT].as<bool>();
+
+  at::Format::Formatter formatter;
+  r = utils::get_formatter(vm, &formatter);
+  if (r < 0) {
+    return r;
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  librbd::Image image;
+  r = utils::init_and_open_image(pool_name, image_name, snap_name, true,
+                                 &rados, &io_ctx, &image);
+  if (r < 0) {
+    return r;
+  }
+
+  return 0;
+}
+
+Shell::SwitchArguments switched_arguments({at::WHOLE_OBJECT});
+Shell::Action action(
+  {"diff"}, {},
+  "Print extents that differ since a previous snap, or image creation.", "",
+  &get_arguments, &execute);
+
+} // namespace diff
+} // namespace action
+} // namespace rbd
diff --git a/src/tools/rbd/action/DiskUsage.cc b/src/tools/rbd/action/DiskUsage.cc
new file mode 100644 (file)
index 0000000..b2cd1cf
--- /dev/null
@@ -0,0 +1,60 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "tools/rbd/ArgumentTypes.h"
+#include "tools/rbd/Shell.h"
+#include "tools/rbd/Utils.h"
+#include "common/Formatter.h"
+#include <iostream>
+#include <boost/program_options.hpp>
+
+namespace rbd {
+namespace action {
+namespace disk_usage {
+
+namespace at = argument_types;
+namespace po = boost::program_options;
+
+void get_arguments(po::options_description *positional,
+                   po::options_description *options) {
+  at::add_image_or_snap_spec_options(positional, options,
+                                     at::ARGUMENT_MODIFIER_NONE);
+  at::add_format_options(options);
+}
+
+int execute(const po::variables_map &vm) {
+  size_t arg_index = 0;
+  std::string pool_name;
+  std::string image_name;
+  std::string snap_name;
+  int r = utils::get_pool_image_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &image_name,
+    &snap_name, utils::SNAPSHOT_PRESENCE_PERMITTED,
+    false);
+  if (r < 0) {
+    return r;
+  }
+
+  at::Format::Formatter formatter;
+  r = utils::get_formatter(vm, &formatter);
+  if (r < 0) {
+    return r;
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  r = utils::init(pool_name, &rados, &io_ctx);
+  if (r < 0) {
+    return r;
+  }
+
+  return 0;
+}
+
+Shell::Action action(
+  {"disk-usage"}, {"du"}, "Show disk usage stats for pool, image or snapshot",
+  "", &get_arguments, &execute);
+
+} // namespace disk_usage
+} // namespace action
+} // namespace rbd
diff --git a/src/tools/rbd/action/Export.cc b/src/tools/rbd/action/Export.cc
new file mode 100644 (file)
index 0000000..9f3b65d
--- /dev/null
@@ -0,0 +1,62 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "tools/rbd/ArgumentTypes.h"
+#include "tools/rbd/Shell.h"
+#include "tools/rbd/Utils.h"
+#include "common/errno.h"
+#include <iostream>
+#include <boost/program_options.hpp>
+
+namespace rbd {
+namespace action {
+namespace export_full {
+
+namespace at = argument_types;
+namespace po = boost::program_options;
+
+void get_arguments(po::options_description *positional,
+                   po::options_description *options) {
+  at::add_image_or_snap_spec_options(positional, options,
+                                     at::ARGUMENT_MODIFIER_SOURCE);
+  at::add_path_options(positional, options,
+                       "export file (or '-' for stdout)");
+  at::add_no_progress_option(options);
+}
+
+int execute(const po::variables_map &vm) {
+  size_t arg_index = 0;
+  std::string pool_name;
+  std::string image_name;
+  std::string snap_name;
+  int r = utils::get_pool_image_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_SOURCE, &arg_index, &pool_name, &image_name,
+    &snap_name, utils::SNAPSHOT_PRESENCE_PERMITTED);
+  if (r < 0) {
+    return r;
+  }
+
+  std::string path;
+  r = utils::get_path(vm, utils::get_positional_argument(vm, 1), &path);
+  if (r < 0) {
+    return r;
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  librbd::Image image;
+  r = utils::init_and_open_image(pool_name, image_name, snap_name, true,
+                                 &rados, &io_ctx, &image);
+  if (r < 0) {
+    return r;
+  }
+
+  return 0;
+}
+
+Shell::Action action(
+  {"export"}, {}, "Export image to file.", "", &get_arguments, &execute);
+
+} // namespace export_full
+} // namespace action
+} // namespace rbd
diff --git a/src/tools/rbd/action/ExportDiff.cc b/src/tools/rbd/action/ExportDiff.cc
new file mode 100644 (file)
index 0000000..2b061ee
--- /dev/null
@@ -0,0 +1,73 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "tools/rbd/ArgumentTypes.h"
+#include "tools/rbd/Shell.h"
+#include "tools/rbd/Utils.h"
+#include "common/errno.h"
+#include <iostream>
+#include <boost/program_options.hpp>
+
+namespace rbd {
+namespace action {
+namespace export_diff {
+
+namespace at = argument_types;
+namespace po = boost::program_options;
+
+void get_arguments(po::options_description *positional,
+                   po::options_description *options) {
+  at::add_image_or_snap_spec_options(positional, options,
+                                     at::ARGUMENT_MODIFIER_SOURCE);
+  at::add_path_options(positional, options,
+                       "export file (or '-' for stdout)");
+  options->add_options()
+    (at::FROM_SNAPSHOT_NAME.c_str(), po::value<std::string>(),
+     "snapshot starting point")
+    (at::WHOLE_OBJECT.c_str(), po::bool_switch(), "compare whole object");
+  at::add_no_progress_option(options);
+}
+
+int execute(const po::variables_map &vm) {
+  size_t arg_index = 0;
+  std::string pool_name;
+  std::string image_name;
+  std::string snap_name;
+  int r = utils::get_pool_image_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_SOURCE, &arg_index, &pool_name, &image_name,
+    &snap_name, utils::SNAPSHOT_PRESENCE_PERMITTED);
+  if (r < 0) {
+    return r;
+  }
+
+  std::string path;
+  r = utils::get_path(vm, utils::get_positional_argument(vm, 1), &path);
+  if (r < 0) {
+    return r;
+  }
+
+  std::string from_snap_name;
+  if (vm.count(at::FROM_SNAPSHOT_NAME)) {
+    from_snap_name = vm[at::FROM_SNAPSHOT_NAME].as<std::string>();
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  librbd::Image image;
+  r = utils::init_and_open_image(pool_name, image_name, snap_name, true,
+                                 &rados, &io_ctx, &image);
+  if (r < 0) {
+    return r;
+  }
+
+  return 0;
+}
+
+Shell::SwitchArguments switched_arguments({at::WHOLE_OBJECT});
+Shell::Action action(
+  {"export-diff"}, {}, "Export incremental diff to file.", "",
+  &get_arguments, &execute);
+
+} // namespace export_diff
+} // namespace action
+} // namespace rbd
diff --git a/src/tools/rbd/action/Feature.cc b/src/tools/rbd/action/Feature.cc
new file mode 100644 (file)
index 0000000..2255b82
--- /dev/null
@@ -0,0 +1,80 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "tools/rbd/ArgumentTypes.h"
+#include "tools/rbd/Shell.h"
+#include "tools/rbd/Utils.h"
+#include "common/errno.h"
+#include <iostream>
+#include <boost/program_options.hpp>
+
+namespace rbd {
+namespace action {
+namespace feature {
+
+namespace at = argument_types;
+namespace po = boost::program_options;
+
+void get_arguments(po::options_description *positional,
+                   po::options_description *options) {
+  at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
+  positional->add_options()
+    ("features", po::value<at::ImageFeatures>()->multitoken(),
+     ("image features\n" + at::get_short_features_help(false)).c_str());
+}
+
+int execute(const po::variables_map &vm, bool enabled) {
+  size_t arg_index = 0;
+  std::string pool_name;
+  std::string image_name;
+  std::string snap_name;
+  int r = utils::get_pool_image_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &image_name,
+    &snap_name, utils::SNAPSHOT_PRESENCE_NONE);
+  if (r < 0) {
+    return r;
+  }
+
+  const std::vector<std::string> &args = vm[at::POSITIONAL_ARGUMENTS]
+    .as<std::vector<std::string> >();
+  std::vector<std::string> feature_names(args.begin() + 1, args.end());
+  if (feature_names.empty()) {
+    std::cerr << "rbd: at least one feature name must be specified"
+              << std::endl;
+    return -EINVAL;
+  }
+
+  boost::any features_any(static_cast<uint64_t>(0));
+  at::ImageFeatures image_features;
+  at::validate(features_any, feature_names, &image_features, 0);
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  librbd::Image image;
+  r = utils::init_and_open_image(pool_name, image_name, "", false,
+                                 &rados, &io_ctx, &image);
+  if (r < 0) {
+    return r;
+  }
+
+  return 0;
+}
+
+int execute_disable(const po::variables_map &vm) {
+  return execute(vm, false);
+}
+
+int execute_enable(const po::variables_map &vm) {
+  return execute(vm, true);
+}
+
+Shell::Action action_disable(
+  {"feature", "disable"}, {}, "Disable the specified image feature.", "",
+  &get_arguments, &execute_disable);
+Shell::Action action_enable(
+  {"feature", "enable"}, {}, "Enable the specified image feature.", "",
+  &get_arguments, &execute_enable);
+
+} // namespace feature
+} // namespace action
+} // namespace rbd
diff --git a/src/tools/rbd/action/Flatten.cc b/src/tools/rbd/action/Flatten.cc
new file mode 100644 (file)
index 0000000..a8d096c
--- /dev/null
@@ -0,0 +1,54 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "tools/rbd/ArgumentTypes.h"
+#include "tools/rbd/Shell.h"
+#include "tools/rbd/Utils.h"
+#include "common/errno.h"
+#include <iostream>
+#include <boost/program_options.hpp>
+
+namespace rbd {
+namespace action {
+namespace flatten {
+
+namespace at = argument_types;
+namespace po = boost::program_options;
+
+void get_arguments(po::options_description *positional,
+                   po::options_description *options) {
+  at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
+  at::add_no_progress_option(options);
+}
+
+int execute(const po::variables_map &vm) {
+  size_t arg_index = 0;
+  std::string pool_name;
+  std::string image_name;
+  std::string snap_name;
+  int r = utils::get_pool_image_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &image_name,
+    &snap_name, utils::SNAPSHOT_PRESENCE_NONE);
+  if (r < 0) {
+    return r;
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  librbd::Image image;
+  r = utils::init_and_open_image(pool_name, image_name, "", false,
+                                 &rados, &io_ctx, &image);
+  if (r < 0) {
+    return r;
+  }
+
+  return 0;
+}
+
+Shell::Action action(
+  {"flatten"}, {}, "Fill clone with parent data (make it independent).", "",
+  &get_arguments, &execute);
+
+} // namespace flatten
+} // namespace action
+} // namespace rbd
diff --git a/src/tools/rbd/action/ImageMeta.cc b/src/tools/rbd/action/ImageMeta.cc
new file mode 100644 (file)
index 0000000..28fc1e5
--- /dev/null
@@ -0,0 +1,206 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "tools/rbd/ArgumentTypes.h"
+#include "tools/rbd/Shell.h"
+#include "tools/rbd/Utils.h"
+#include "common/errno.h"
+#include "common/Formatter.h"
+#include <iostream>
+#include <boost/program_options.hpp>
+
+namespace rbd {
+namespace action {
+namespace image_meta {
+
+namespace at = argument_types;
+namespace po = boost::program_options;
+
+namespace {
+
+void add_key_option(po::options_description *positional) {
+  positional->add_options()
+    ("key", "image meta key");
+}
+
+int get_key(const po::variables_map &vm, std::string *key) {
+  *key = utils::get_positional_argument(vm, 1);
+  if (key->empty()) {
+    std::cerr << "rbd: metadata key was not specified" << std::endl;
+    return -EINVAL;
+  }
+  return 0;
+}
+
+} // anonymous namespace
+
+void get_list_arguments(po::options_description *positional,
+                        po::options_description *options) {
+  at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
+  at::add_format_options(options);
+}
+
+int execute_list(const po::variables_map &vm) {
+  size_t arg_index = 0;
+  std::string pool_name;
+  std::string image_name;
+  std::string snap_name;
+  int r = utils::get_pool_image_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &image_name,
+    &snap_name, utils::SNAPSHOT_PRESENCE_NONE);
+  if (r < 0) {
+    return r;
+  }
+
+  at::Format::Formatter formatter;
+  r = utils::get_formatter(vm, &formatter);
+  if (r < 0) {
+    return r;
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  librbd::Image image;
+  r = utils::init_and_open_image(pool_name, image_name, "", false,
+                                 &rados, &io_ctx, &image);
+  if (r < 0) {
+    return r;
+  }
+
+  return 0;
+}
+
+void get_get_arguments(po::options_description *positional,
+                       po::options_description *options) {
+  at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
+  add_key_option(positional);
+}
+
+int execute_get(const po::variables_map &vm) {
+  size_t arg_index = 0;
+  std::string pool_name;
+  std::string image_name;
+  std::string snap_name;
+  int r = utils::get_pool_image_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &image_name,
+    &snap_name, utils::SNAPSHOT_PRESENCE_NONE);
+  if (r < 0) {
+    return r;
+  }
+
+  std::string key;
+  r = get_key(vm, &key);
+  if (r < 0) {
+    return r;
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  librbd::Image image;
+  r = utils::init_and_open_image(pool_name, image_name, "", false,
+                                 &rados, &io_ctx, &image);
+  if (r < 0) {
+    return r;
+  }
+
+  return 0;
+}
+
+void get_set_arguments(po::options_description *positional,
+                       po::options_description *options) {
+  at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
+  add_key_option(positional);
+  positional->add_options()
+    ("value", "image meta value");
+}
+
+int execute_set(const po::variables_map &vm) {
+  size_t arg_index = 0;
+  std::string pool_name;
+  std::string image_name;
+  std::string snap_name;
+  int r = utils::get_pool_image_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &image_name,
+    &snap_name, utils::SNAPSHOT_PRESENCE_NONE);
+  if (r < 0) {
+    return r;
+  }
+
+  std::string key;
+  r = get_key(vm, &key);
+  if (r < 0) {
+    return r;
+  }
+
+  std::string value = utils::get_positional_argument(vm, 2);
+  if (value.empty()) {
+    std::cerr << "rbd: metadata value was not specified" << std::endl;
+    return -EINVAL;
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  librbd::Image image;
+  r = utils::init_and_open_image(pool_name, image_name, "", false,
+                                 &rados, &io_ctx, &image);
+  if (r < 0) {
+    return r;
+  }
+
+  return 0;
+}
+
+void get_remove_arguments(po::options_description *positional,
+                          po::options_description *options) {
+  at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
+  add_key_option(positional);
+}
+
+int execute_remove(const po::variables_map &vm) {
+  size_t arg_index = 0;
+  std::string pool_name;
+  std::string image_name;
+  std::string snap_name;
+  int r = utils::get_pool_image_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &image_name,
+    &snap_name, utils::SNAPSHOT_PRESENCE_NONE);
+  if (r < 0) {
+    return r;
+  }
+
+  std::string key;
+  r = get_key(vm, &key);
+  if (r < 0) {
+    return r;
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  librbd::Image image;
+  r = utils::init_and_open_image(pool_name, image_name, "", false,
+                                 &rados, &io_ctx, &image);
+  if (r < 0) {
+    return r;
+  }
+
+  return 0;
+}
+
+Shell::Action action_list(
+  {"image-meta", "list"}, {}, "Image metadata list keys with values.", "",
+  &get_list_arguments, &execute_list);
+Shell::Action action_get(
+  {"image-meta", "get"}, {},
+  "Image metadata get the value associated with the key.", "",
+  &get_get_arguments, &execute_get);
+Shell::Action action_set(
+  {"image-meta", "set"}, {}, "Image metadata set key with value.", "",
+  &get_set_arguments, &execute_set);
+Shell::Action action_remove(
+  {"image-meta", "remove"}, {},
+  "Image metadata remove the key and value associated.", "",
+  &get_remove_arguments, &execute_remove);
+
+} // namespace image_meta
+} // namespace action
+} // namespace rbd
diff --git a/src/tools/rbd/action/Import.cc b/src/tools/rbd/action/Import.cc
new file mode 100644 (file)
index 0000000..f5dd141
--- /dev/null
@@ -0,0 +1,99 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "tools/rbd/ArgumentTypes.h"
+#include "tools/rbd/Shell.h"
+#include "tools/rbd/Utils.h"
+#include "common/errno.h"
+#include <iostream>
+#include <boost/program_options.hpp>
+
+namespace rbd {
+namespace action {
+namespace import {
+
+namespace at = argument_types;
+namespace po = boost::program_options;
+
+void get_arguments(po::options_description *positional,
+                   po::options_description *options) {
+  at::add_path_options(positional, options,
+                       "import file (or '-' for stdin)");
+  at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_DEST);
+  at::add_create_image_options(options, true);
+  at::add_no_progress_option(options);
+
+  // TODO legacy rbd allowed import to accept both 'image'/'dest' and
+  //      'pool'/'dest-pool'
+  at::add_pool_option(options, at::ARGUMENT_MODIFIER_NONE, " (deprecated)");
+  at::add_image_option(options, at::ARGUMENT_MODIFIER_NONE, " (deprecated)");
+}
+
+int execute(const po::variables_map &vm) {
+  std::string path;
+  int r = utils::get_path(vm, utils::get_positional_argument(vm, 0), &path);
+  if (r < 0) {
+    return r;
+  }
+
+  // odd check to support legacy / deprecated behavior of import
+  std::string deprecated_pool_name;
+  if (vm.count(at::POOL_NAME)) {
+    deprecated_pool_name = vm[at::POOL_NAME].as<std::string>();
+    std::cerr << "rbd: --pool is deprecated for import, use --dest-pool"
+              << std::endl;
+  }
+
+  std::string deprecated_image_name;
+  if (vm.count(at::IMAGE_NAME)) {
+    utils::extract_spec(vm[at::IMAGE_NAME].as<std::string>(),
+                        &deprecated_pool_name, &deprecated_image_name, nullptr);
+    std::cerr << "rbd: --image is deprecated for import, use --dest"
+              << std::endl;
+  } else {
+    deprecated_image_name = path.substr(path.find_last_of("/") + 1);
+  }
+
+  size_t arg_index = 1;
+  std::string pool_name = deprecated_pool_name;
+  std::string image_name;
+  std::string snap_name;
+  r = utils::get_pool_image_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_DEST, &arg_index, &pool_name, &image_name,
+    &snap_name, utils::SNAPSHOT_PRESENCE_NONE, false);
+  if (r < 0) {
+    return r;
+  }
+
+  if (image_name.empty()) {
+    image_name = deprecated_image_name;
+  }
+
+  int order;
+  uint32_t format;
+  uint64_t features;
+  uint32_t stripe_unit;
+  uint32_t stripe_count;
+  r = utils::get_image_options(vm, &order, &format, &features, &stripe_unit,
+                               &stripe_count);
+  if (r < 0) {
+    return r;
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  r = utils::init(pool_name, &rados, &io_ctx);
+  if (r < 0) {
+    return r;
+  }
+
+  return 0;
+}
+
+Shell::Action action(
+  {"import"}, {}, "Import image from file.", at::get_long_features_help(),
+  &get_arguments, &execute);
+
+} // namespace import
+} // namespace action
+} // namespace rbd
diff --git a/src/tools/rbd/action/ImportDiff.cc b/src/tools/rbd/action/ImportDiff.cc
new file mode 100644 (file)
index 0000000..82e0a83
--- /dev/null
@@ -0,0 +1,64 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "tools/rbd/ArgumentTypes.h"
+#include "tools/rbd/Shell.h"
+#include "tools/rbd/Utils.h"
+#include "common/errno.h"
+#include <iostream>
+#include <boost/program_options.hpp>
+
+#define dout_subsys ceph_subsys_rbd
+
+namespace rbd {
+namespace action {
+namespace import_diff {
+
+namespace at = argument_types;
+namespace po = boost::program_options;
+
+void get_arguments(po::options_description *positional,
+                   po::options_description *options) {
+  at::add_path_options(positional, options,
+                       "import file (or '-' for stdin)");
+  at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
+  at::add_no_progress_option(options);
+}
+
+int execute(const po::variables_map &vm) {
+  std::string path;
+  int r = utils::get_path(vm, utils::get_positional_argument(vm, 0), &path);
+  if (r < 0) {
+    return r;
+  }
+
+  size_t arg_index = 1;
+  std::string pool_name;
+  std::string image_name;
+  std::string snap_name;
+  r = utils::get_pool_image_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &image_name,
+    &snap_name, utils::SNAPSHOT_PRESENCE_NONE);
+  if (r < 0) {
+    return r;
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  librbd::Image image;
+  r = utils::init_and_open_image(pool_name, image_name, "", false,
+                                 &rados, &io_ctx, &image);
+  if (r < 0) {
+    return r;
+  }
+
+  return 0;
+}
+
+Shell::Action action(
+  {"import-diff"}, {}, "Import an incremental diff.", "", &get_arguments,
+  &execute);
+
+} // namespace list
+} // namespace action
+} // namespace rbd
diff --git a/src/tools/rbd/action/Info.cc b/src/tools/rbd/action/Info.cc
new file mode 100644 (file)
index 0000000..023951f
--- /dev/null
@@ -0,0 +1,61 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "tools/rbd/ArgumentTypes.h"
+#include "tools/rbd/Shell.h"
+#include "tools/rbd/Utils.h"
+#include "common/Formatter.h"
+#include <iostream>
+#include <boost/program_options.hpp>
+
+namespace rbd {
+namespace action {
+namespace info {
+
+namespace at = argument_types;
+namespace po = boost::program_options;
+
+void get_arguments(po::options_description *positional,
+                   po::options_description *options) {
+  at::add_image_or_snap_spec_options(positional, options,
+                                     at::ARGUMENT_MODIFIER_NONE);
+  at::add_format_options(options);
+}
+
+int execute(const po::variables_map &vm) {
+  size_t arg_index = 0;
+  std::string pool_name;
+  std::string image_name;
+  std::string snap_name;
+  int r = utils::get_pool_image_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &image_name,
+    &snap_name, utils::SNAPSHOT_PRESENCE_PERMITTED);
+  if (r < 0) {
+    return r;
+  }
+
+  at::Format::Formatter formatter;
+  r = utils::get_formatter(vm, &formatter);
+  if (r < 0) {
+    return r;
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  librbd::Image image;
+  r = utils::init_and_open_image(pool_name, image_name, snap_name, true,
+                                 &rados, &io_ctx, &image);
+  if (r < 0) {
+    return r;
+  }
+
+  return 0;
+}
+
+Shell::Action action(
+  {"info"}, {}, "Show information about image size, striping, etc.", "",
+  &get_arguments, &execute);
+
+} // namespace info
+} // namespace action
+} // namespace rbd
diff --git a/src/tools/rbd/action/Kernel.cc b/src/tools/rbd/action/Kernel.cc
new file mode 100644 (file)
index 0000000..846b5f5
--- /dev/null
@@ -0,0 +1,122 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "tools/rbd/ArgumentTypes.h"
+#include "tools/rbd/Shell.h"
+#include "tools/rbd/Utils.h"
+#include "common/errno.h"
+#include "common/Formatter.h"
+#include <iostream>
+#include <boost/algorithm/string/predicate.hpp>
+#include <boost/program_options.hpp>
+#include <boost/scope_exit.hpp>
+
+namespace rbd {
+namespace action {
+namespace kernel {
+
+namespace at = argument_types;
+namespace po = boost::program_options;
+
+void get_show_arguments(po::options_description *positional,
+                        po::options_description *options) {
+  at::add_format_options(options);
+}
+
+int execute_show(const po::variables_map &vm) {
+  at::Format::Formatter formatter;
+  int r = utils::get_formatter(vm, &formatter);
+  if (r < 0) {
+    return r;
+  }
+
+  utils::init_context();
+
+  return 0;
+}
+
+void get_map_arguments(po::options_description *positional,
+                       po::options_description *options) {
+  at::add_image_or_snap_spec_options(positional, options,
+                                     at::ARGUMENT_MODIFIER_NONE);
+  options->add_options()
+    ("options,o", po::value<std::string>(), "mapping options")
+    ("read-only", po::bool_switch(), "mount read-only");
+}
+
+int execute_map(const po::variables_map &vm) {
+  size_t arg_index = 0;
+  std::string pool_name;
+  std::string image_name;
+  std::string snap_name;
+  int r = utils::get_pool_image_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &image_name,
+    &snap_name, utils::SNAPSHOT_PRESENCE_PERMITTED);
+  if (r < 0) {
+    return r;
+  }
+
+  utils::init_context();
+
+  return 0;
+}
+
+void get_unmap_arguments(po::options_description *positional,
+                   po::options_description *options) {
+  positional->add_options()
+    ("image-or-snap-or-device-spec",
+     "image, snapshot, or device specification\n"
+     "[<pool-name>/]<image-name>[@<snapshot-name>] or <device-path>");
+  at::add_pool_option(options, at::ARGUMENT_MODIFIER_NONE);
+  at::add_image_option(options, at::ARGUMENT_MODIFIER_NONE);
+  at::add_snap_option(options, at::ARGUMENT_MODIFIER_NONE);
+}
+
+int execute_unmap(const po::variables_map &vm) {
+  std::string device_name = utils::get_positional_argument(vm, 0);
+  if (!boost::starts_with(device_name, "/dev/")) {
+    device_name.clear();
+  }
+
+  size_t arg_index = 0;
+  std::string pool_name;
+  std::string image_name;
+  std::string snap_name;
+  int r;
+  if (device_name.empty()) {
+    r = utils::get_pool_image_snapshot_names(
+      vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &image_name,
+      &snap_name, utils::SNAPSHOT_PRESENCE_PERMITTED,
+      false);
+    if (r < 0) {
+      return r;
+    }
+  }
+
+  if (device_name.empty() && image_name.empty()) {
+    std::cerr << "rbd: unmap requires either image name or device path"
+              << std::endl;
+    return -EINVAL;
+  }
+
+  utils::init_context();
+
+  return 0;
+}
+
+Shell::SwitchArguments switched_arguments({"read-only"});
+Shell::Action action_show(
+  {"showmapped"}, {}, "Show the rbd images mapped by the kernel.", "",
+  &get_show_arguments, &execute_show);
+
+Shell::Action action_map(
+  {"map"}, {}, "Map image to a block device using the kernel.", "",
+  &get_map_arguments, &execute_map);
+
+Shell::Action action_unmap(
+  {"unmap"}, {}, "Unmap a rbd device that was used by the kernel.", "",
+  &get_unmap_arguments, &execute_unmap);
+
+} // namespace kernel
+} // namespace action
+} // namespace rbd
diff --git a/src/tools/rbd/action/List.cc b/src/tools/rbd/action/List.cc
new file mode 100644 (file)
index 0000000..f176d7b
--- /dev/null
@@ -0,0 +1,61 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "tools/rbd/ArgumentTypes.h"
+#include "tools/rbd/Shell.h"
+#include "tools/rbd/Utils.h"
+#include "common/errno.h"
+#include "common/Formatter.h"
+#include <iostream>
+#include <boost/program_options.hpp>
+
+namespace rbd {
+namespace action {
+namespace list {
+
+namespace at = argument_types;
+namespace po = boost::program_options;
+
+void get_arguments(po::options_description *positional,
+                   po::options_description *options) {
+  positional->add_options()
+    ("pool-name", "pool name");
+  options->add_options()
+    ("long,l", po::bool_switch(), "long listing format")
+    ("pool,p", po::value<std::string>(), "pool name");
+  at::add_format_options(options);
+}
+
+int execute(const po::variables_map &vm) {
+  std::string pool_name = utils::get_positional_argument(vm, 0);
+  if (pool_name.empty() && vm.count("pool")) {
+    pool_name = vm["pool"].as<std::string>();
+  }
+
+  if (pool_name.empty()) {
+    pool_name = at::DEFAULT_POOL_NAME;
+  }
+
+  at::Format::Formatter formatter;
+  int r = utils::get_formatter(vm, &formatter);
+  if (r < 0) {
+    return r;
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  r = utils::init(pool_name, &rados, &io_ctx);
+  if (r < 0) {
+    return r;
+  }
+
+  return 0;
+}
+
+Shell::SwitchArguments switched_arguments({"long", "l"});
+Shell::Action action(
+  {"list"}, {"ls"}, "List rbd images.", "", &get_arguments, &execute);
+
+} // namespace list
+} // namespace action
+} // namespace rbd
diff --git a/src/tools/rbd/action/Lock.cc b/src/tools/rbd/action/Lock.cc
new file mode 100644 (file)
index 0000000..ac0ae40
--- /dev/null
@@ -0,0 +1,171 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "tools/rbd/ArgumentTypes.h"
+#include "tools/rbd/Shell.h"
+#include "tools/rbd/Utils.h"
+#include "common/Formatter.h"
+#include <iostream>
+#include <boost/program_options.hpp>
+
+namespace rbd {
+namespace action {
+namespace lock {
+
+namespace at = argument_types;
+namespace po = boost::program_options;
+
+namespace {
+
+void add_id_option(po::options_description *positional) {
+  positional->add_options()
+    ("lock-id", "unique lock id");
+}
+
+int get_id(const po::variables_map &vm, std::string *id) {
+  *id = utils::get_positional_argument(vm, 1);
+  if (id->empty()) {
+    std::cerr << "rbd: lock id was not specified" << std::endl;
+    return -EINVAL;
+  }
+  return 0;
+}
+
+} // anonymous namespace
+
+void get_list_arguments(po::options_description *positional,
+                        po::options_description *options) {
+  at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
+  at::add_format_options(options);
+}
+
+int execute_list(const po::variables_map &vm) {
+  size_t arg_index = 0;
+  std::string pool_name;
+  std::string image_name;
+  std::string snap_name;
+  int r = utils::get_pool_image_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &image_name,
+    &snap_name, utils::SNAPSHOT_PRESENCE_NONE);
+  if (r < 0) {
+    return r;
+  }
+
+  at::Format::Formatter formatter;
+  r = utils::get_formatter(vm, &formatter);
+  if (r < 0) {
+    return r;
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  librbd::Image image;
+  r = utils::init_and_open_image(pool_name, image_name, "", true,
+                                 &rados, &io_ctx, &image);
+  if (r < 0) {
+    return r;
+  }
+
+  return 0;
+}
+
+void get_add_arguments(po::options_description *positional,
+                       po::options_description *options) {
+  at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
+  add_id_option(positional);
+  options->add_options()
+    ("shared", po::value<std::string>(), "shared lock tag");
+}
+
+int execute_add(const po::variables_map &vm) {
+  size_t arg_index = 0;
+  std::string pool_name;
+  std::string image_name;
+  std::string snap_name;
+  int r = utils::get_pool_image_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &image_name,
+    &snap_name, utils::SNAPSHOT_PRESENCE_NONE);
+  if (r < 0) {
+    return r;
+  }
+
+  std::string lock_cookie;
+  r = get_id(vm, &lock_cookie);
+  if (r < 0) {
+    return r;
+  }
+
+  std::string lock_tag;
+  if (vm.count("shared")) {
+    lock_tag = vm["shared"].as<std::string>();
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  librbd::Image image;
+  r = utils::init_and_open_image(pool_name, image_name, "", false,
+                                 &rados, &io_ctx, &image);
+  if (r < 0) {
+    return r;
+  }
+
+  return 0;
+}
+
+void get_remove_arguments(po::options_description *positional,
+                          po::options_description *options) {
+  at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
+  add_id_option(positional);
+  positional->add_options()
+    ("locker", "locker client");
+}
+
+int execute_remove(const po::variables_map &vm) {
+  size_t arg_index = 0;
+  std::string pool_name;
+  std::string image_name;
+  std::string snap_name;
+  int r = utils::get_pool_image_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &image_name,
+    &snap_name, utils::SNAPSHOT_PRESENCE_NONE);
+  if (r < 0) {
+    return r;
+  }
+
+  std::string lock_cookie;
+  r = get_id(vm, &lock_cookie);
+  if (r < 0) {
+    return r;
+  }
+
+  std::string lock_client = utils::get_positional_argument(vm, 2);
+  if (lock_client.empty()) {
+    std::cerr << "rbd: locker was not specified" << std::endl;
+    return -EINVAL;
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  librbd::Image image;
+  r = utils::init_and_open_image(pool_name, image_name, "", false,
+                                 &rados, &io_ctx, &image);
+  if (r < 0) {
+    return r;
+  }
+
+  return 0;
+}
+
+Shell::Action action_list(
+  {"lock", "list"}, {}, "Show locks held on an image.", "",
+  &get_list_arguments, &execute_list);
+Shell::Action action_add(
+  {"lock", "add"}, {}, "Take a lock on an image.", "",
+  &get_add_arguments, &execute_add);
+Shell::Action action_remove(
+  {"lock", "remove"}, {"lock", "rm"}, "Release a lock on an image.", "",
+  &get_remove_arguments, &execute_remove);
+
+} // namespace lock
+} // namespace action
+} // namespace rbd
diff --git a/src/tools/rbd/action/MergeDiff.cc b/src/tools/rbd/action/MergeDiff.cc
new file mode 100644 (file)
index 0000000..b06276f
--- /dev/null
@@ -0,0 +1,59 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "tools/rbd/ArgumentTypes.h"
+#include "tools/rbd/Shell.h"
+#include "tools/rbd/Utils.h"
+#include "common/errno.h"
+#include <iostream>
+#include <boost/program_options.hpp>
+
+#define dout_subsys ceph_subsys_rbd
+
+namespace rbd {
+namespace action {
+namespace merge_diff {
+
+namespace at = argument_types;
+namespace po = boost::program_options;
+
+void get_arguments(po::options_description *positional,
+                   po::options_description *options) {
+  positional->add_options()
+    ("diff1-path", "path to first diff (or '-' for stdin)")
+    ("diff2-path", "path to second diff");
+  at::add_path_options(positional, options,
+                       "path to merged diff (or '-' for stdout)");
+  at::add_no_progress_option(options);
+}
+
+int execute(const po::variables_map &vm) {
+  std::string first_diff = utils::get_positional_argument(vm, 0);
+  if (first_diff.empty()) {
+    std::cerr << "rbd: first diff was not specified" << std::endl;
+    return -EINVAL;
+  }
+
+  std::string second_diff = utils::get_positional_argument(vm, 1);
+  if (second_diff.empty()) {
+    std::cerr << "rbd: second diff was not specified" << std::endl;
+    return -EINVAL;
+  }
+
+  std::string path;
+  int r = utils::get_path(vm, utils::get_positional_argument(vm, 2),
+                          &path);
+  if (r < 0) {
+    return r;
+  }
+
+  return 0;
+}
+
+Shell::Action action(
+  {"merge-diff"}, {}, "Merge two diff exports together.", "",
+  &get_arguments, &execute);
+
+} // namespace merge_diff
+} // namespace action
+} // namespace rbd
diff --git a/src/tools/rbd/action/ObjectMap.cc b/src/tools/rbd/action/ObjectMap.cc
new file mode 100644 (file)
index 0000000..ab23dc6
--- /dev/null
@@ -0,0 +1,55 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "tools/rbd/ArgumentTypes.h"
+#include "tools/rbd/Shell.h"
+#include "tools/rbd/Utils.h"
+#include "common/errno.h"
+#include <iostream>
+#include <boost/program_options.hpp>
+
+namespace rbd {
+namespace action {
+namespace object_map {
+
+namespace at = argument_types;
+namespace po = boost::program_options;
+
+void get_arguments(po::options_description *positional,
+                   po::options_description *options) {
+  at::add_image_or_snap_spec_options(positional, options,
+                                     at::ARGUMENT_MODIFIER_NONE);
+  at::add_no_progress_option(options);
+}
+
+int execute(const po::variables_map &vm) {
+  size_t arg_index = 0;
+  std::string pool_name;
+  std::string image_name;
+  std::string snap_name;
+  int r = utils::get_pool_image_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &image_name,
+    &snap_name, utils::SNAPSHOT_PRESENCE_PERMITTED);
+  if (r < 0) {
+    return r;
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  librbd::Image image;
+  r = utils::init_and_open_image(pool_name, image_name, snap_name, false,
+                                 &rados, &io_ctx, &image);
+  if (r < 0) {
+    return r;
+  }
+
+  return 0;
+}
+
+Shell::Action action(
+  {"object-map", "rebuild"}, {}, "Rebuild an invalid object map.", "",
+  &get_arguments, &execute);
+
+} // namespace object_map
+} // namespace action
+} // namespace rbd
diff --git a/src/tools/rbd/action/Remove.cc b/src/tools/rbd/action/Remove.cc
new file mode 100644 (file)
index 0000000..f7244c9
--- /dev/null
@@ -0,0 +1,51 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "tools/rbd/ArgumentTypes.h"
+#include "tools/rbd/Shell.h"
+#include "tools/rbd/Utils.h"
+#include "common/errno.h"
+#include <iostream>
+#include <boost/program_options.hpp>
+
+namespace rbd {
+namespace action {
+namespace remove {
+
+namespace at = argument_types;
+namespace po = boost::program_options;
+
+void get_arguments(po::options_description *positional,
+                   po::options_description *options) {
+  at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
+  at::add_no_progress_option(options);
+}
+
+int execute(const po::variables_map &vm) {
+  size_t arg_index = 0;
+  std::string pool_name;
+  std::string image_name;
+  std::string snap_name;
+  int r = utils::get_pool_image_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &image_name,
+    &snap_name, utils::SNAPSHOT_PRESENCE_NONE);
+  if (r < 0) {
+    return r;
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  r = utils::init(pool_name, &rados, &io_ctx);
+  if (r < 0) {
+    return r;
+  }
+
+  return 0;
+}
+
+Shell::Action action(
+  {"remove"}, {"rm"}, "Delete an image.", "", &get_arguments, &execute);
+
+} // namespace remove
+} // namespace action
+} // namespace rbd
diff --git a/src/tools/rbd/action/Rename.cc b/src/tools/rbd/action/Rename.cc
new file mode 100644 (file)
index 0000000..a600c5f
--- /dev/null
@@ -0,0 +1,69 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "tools/rbd/ArgumentTypes.h"
+#include "tools/rbd/Shell.h"
+#include "tools/rbd/Utils.h"
+#include "common/errno.h"
+#include <iostream>
+#include <boost/program_options.hpp>
+
+namespace rbd {
+namespace action {
+namespace rename {
+
+namespace at = argument_types;
+namespace po = boost::program_options;
+
+void get_arguments(po::options_description *positional,
+                   po::options_description *options) {
+  at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_SOURCE);
+  at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_DEST);
+}
+
+int execute(const po::variables_map &vm) {
+  size_t arg_index = 0;
+  std::string pool_name;
+  std::string image_name;
+  std::string snap_name;
+  int r = utils::get_pool_image_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_SOURCE, &arg_index, &pool_name, &image_name,
+    &snap_name, utils::SNAPSHOT_PRESENCE_NONE);
+  if (r < 0) {
+    return r;
+  }
+
+  std::string dst_pool_name;
+  std::string dst_image_name;
+  std::string dst_snap_name;
+  r = utils::get_pool_image_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_DEST, &arg_index, &dst_pool_name, &dst_image_name,
+    &dst_snap_name, utils::SNAPSHOT_PRESENCE_NONE);
+  if (r < 0) {
+    return r;
+  }
+
+  if (pool_name != dst_pool_name) {
+    std::cerr << "rbd: mv/rename across pools not supported" << std::endl
+              << "source pool: " << pool_name<< " dest pool: " << dst_pool_name
+              << std::endl;
+    return -EINVAL;
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  r = utils::init(pool_name, &rados, &io_ctx);
+  if (r < 0) {
+    return r;
+  }
+
+  return 0;
+}
+
+Shell::Action action(
+  {"rename"}, {"mv"}, "Rename image within pool.", "", &get_arguments,
+  &execute);
+
+} // namespace list
+} // namespace action
+} // namespace rbd
diff --git a/src/tools/rbd/action/Resize.cc b/src/tools/rbd/action/Resize.cc
new file mode 100644 (file)
index 0000000..a2ad878
--- /dev/null
@@ -0,0 +1,77 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "tools/rbd/ArgumentTypes.h"
+#include "tools/rbd/Shell.h"
+#include "tools/rbd/Utils.h"
+#include "common/errno.h"
+#include <iostream>
+#include <boost/program_options.hpp>
+
+namespace rbd {
+namespace action {
+namespace resize {
+
+namespace at = argument_types;
+namespace po = boost::program_options;
+
+void get_arguments(po::options_description *positional,
+                   po::options_description *options) {
+  at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
+  at::add_size_option(options);
+  options->add_options()
+    ("allow-shrink", po::bool_switch(), "permit shrinking");
+  at::add_no_progress_option(options);
+}
+
+int execute(const po::variables_map &vm) {
+  size_t arg_index = 0;
+  std::string pool_name;
+  std::string image_name;
+  std::string snap_name;
+  int r = utils::get_pool_image_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &image_name,
+    &snap_name, utils::SNAPSHOT_PRESENCE_NONE);
+  if (r < 0) {
+    return r;
+  }
+
+  uint64_t size;
+  r = utils::get_image_size(vm, &size);
+  if (r < 0) {
+    return r;
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  librbd::Image image;
+  r = utils::init_and_open_image(pool_name, image_name, snap_name, false,
+                                 &rados, &io_ctx, &image);
+  if (r < 0) {
+    return r;
+  }
+
+  librbd::image_info_t info;
+  r = image.stat(info, sizeof(info));
+  if (r < 0) {
+    std::cerr << "rbd: resize error: " << cpp_strerror(r) << std::endl;
+    return r;
+  }
+
+  if (info.size > size && !vm["allow-shrink"].as<bool>()) {
+    std::cerr << "rbd: shrinking an image is only allowed with the "
+              << "--allow-shrink flag" << std::endl;
+    return -EINVAL;
+  }
+
+  return 0;
+}
+
+Shell::SwitchArguments switched_arguments({"allow-shrink"});
+Shell::Action action(
+  {"resize"}, {}, "Resize (expand or shrink) image.", "", &get_arguments,
+  &execute);
+
+} // namespace list
+} // namespace action
+} // namespace rbd
diff --git a/src/tools/rbd/action/Snap.cc b/src/tools/rbd/action/Snap.cc
new file mode 100644 (file)
index 0000000..a73a693
--- /dev/null
@@ -0,0 +1,255 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "tools/rbd/ArgumentTypes.h"
+#include "tools/rbd/Shell.h"
+#include "tools/rbd/Utils.h"
+#include "common/errno.h"
+#include "common/Formatter.h"
+#include <iostream>
+#include <boost/program_options.hpp>
+
+namespace rbd {
+namespace action {
+namespace snap {
+
+namespace at = argument_types;
+namespace po = boost::program_options;
+
+void get_list_arguments(po::options_description *positional,
+                        po::options_description *options) {
+  at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
+  at::add_format_options(options);
+}
+
+int execute_list(const po::variables_map &vm) {
+  size_t arg_index = 0;
+  std::string pool_name;
+  std::string image_name;
+  std::string snap_name;
+  int r = utils::get_pool_image_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &image_name,
+    &snap_name, utils::SNAPSHOT_PRESENCE_NONE);
+  if (r < 0) {
+    return r;
+  }
+
+  at::Format::Formatter formatter;
+  r = utils::get_formatter(vm, &formatter);
+  if (r < 0) {
+    return r;
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  librbd::Image image;
+  r = utils::init_and_open_image(pool_name, image_name, "", false, &rados,
+                                 &io_ctx, &image);
+  if (r < 0) {
+    return r;
+  }
+
+  return 0;
+}
+
+void get_create_arguments(po::options_description *positional,
+                          po::options_description *options) {
+  at::add_snap_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
+}
+
+int execute_create(const po::variables_map &vm) {
+  size_t arg_index = 0;
+  std::string pool_name;
+  std::string image_name;
+  std::string snap_name;
+  int r = utils::get_pool_image_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &image_name,
+    &snap_name, utils::SNAPSHOT_PRESENCE_REQUIRED);
+  if (r < 0) {
+    return r;
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  librbd::Image image;
+  r = utils::init_and_open_image(pool_name, image_name, "", false, &rados,
+                                 &io_ctx, &image);
+  if (r < 0) {
+    return r;
+  }
+
+  return 0;
+}
+
+void get_remove_arguments(po::options_description *positional,
+                          po::options_description *options) {
+  at::add_snap_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
+}
+
+int execute_remove(const po::variables_map &vm) {
+  size_t arg_index = 0;
+  std::string pool_name;
+  std::string image_name;
+  std::string snap_name;
+  int r = utils::get_pool_image_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &image_name,
+    &snap_name, utils::SNAPSHOT_PRESENCE_REQUIRED);
+  if (r < 0) {
+    return r;
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  librbd::Image image;
+  r = utils::init_and_open_image(pool_name, image_name, "", false, &rados,
+                                 &io_ctx, &image);
+  if (r < 0) {
+    return r;
+  }
+
+  return 0;
+}
+
+void get_purge_arguments(po::options_description *positional,
+                         po::options_description *options) {
+  at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
+  at::add_no_progress_option(options);
+}
+
+int execute_purge(const po::variables_map &vm) {
+  size_t arg_index = 0;
+  std::string pool_name;
+  std::string image_name;
+  std::string snap_name;
+  int r = utils::get_pool_image_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &image_name,
+    &snap_name, utils::SNAPSHOT_PRESENCE_NONE);
+  if (r < 0) {
+    return r;
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  librbd::Image image;
+  r = utils::init_and_open_image(pool_name, image_name, "", false, &rados,
+                                 &io_ctx, &image);
+  if (r < 0) {
+    return r;
+  }
+
+  return 0;
+}
+
+void get_rollback_arguments(po::options_description *positional,
+                            po::options_description *options) {
+  at::add_snap_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
+  at::add_no_progress_option(options);
+}
+
+int execute_rollback(const po::variables_map &vm) {
+  size_t arg_index = 0;
+  std::string pool_name;
+  std::string image_name;
+  std::string snap_name;
+  int r = utils::get_pool_image_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &image_name,
+    &snap_name, utils::SNAPSHOT_PRESENCE_REQUIRED);
+  if (r < 0) {
+    return r;
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  librbd::Image image;
+  r = utils::init_and_open_image(pool_name, image_name, "", false, &rados,
+                                 &io_ctx, &image);
+  if (r < 0) {
+    return r;
+  }
+
+  return 0;
+}
+
+void get_protect_arguments(po::options_description *positional,
+                           po::options_description *options) {
+  at::add_snap_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
+}
+
+int execute_protect(const po::variables_map &vm) {
+  size_t arg_index = 0;
+  std::string pool_name;
+  std::string image_name;
+  std::string snap_name;
+  int r = utils::get_pool_image_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &image_name,
+    &snap_name, utils::SNAPSHOT_PRESENCE_REQUIRED);
+  if (r < 0) {
+    return r;
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  librbd::Image image;
+  r = utils::init_and_open_image(pool_name, image_name, "", false, &rados,
+                                 &io_ctx, &image);
+  if (r < 0) {
+    return r;
+  }
+
+  return 0;
+}
+
+void get_unprotect_arguments(po::options_description *positional,
+                             po::options_description *options) {
+  at::add_snap_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
+}
+
+int execute_unprotect(const po::variables_map &vm) {
+  size_t arg_index = 0;
+  std::string pool_name;
+  std::string image_name;
+  std::string snap_name;
+  int r = utils::get_pool_image_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &image_name,
+    &snap_name, utils::SNAPSHOT_PRESENCE_REQUIRED);
+  if (r < 0) {
+    return r;
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  librbd::Image image;
+  r = utils::init_and_open_image(pool_name, image_name, "", false, &rados,
+                                 &io_ctx, &image);
+  if (r < 0) {
+    return r;
+  }
+
+  return 0;
+}
+
+Shell::Action action_list(
+  {"snap", "ls"}, {}, "Dump list of image snapshots.", "",
+  &get_list_arguments, &execute_list);
+Shell::Action action_create(
+  {"snap", "create"}, {}, "Create a snapshot.", "",
+  &get_create_arguments, &execute_create);
+Shell::Action action_remove(
+  {"snap", "remove"}, {"snap", "rm"}, "Deletes a snapshot.", "",
+  &get_remove_arguments, &execute_remove);
+Shell::Action action_purge(
+  {"snap", "purge"}, {}, "Deletes all snapshots.", "",
+  &get_purge_arguments, &execute_purge);
+Shell::Action action_rollback(
+  {"snap", "rollback"}, {}, "Rollback image to snapshot.", "",
+  &get_rollback_arguments, &execute_rollback);
+Shell::Action action_protect(
+  {"snap", "protect"}, {}, "Prevent a snapshot from being deleted.", "",
+  &get_protect_arguments, &execute_protect);
+Shell::Action action_unprotect(
+  {"snap", "unprotect"}, {}, "Allow a snapshot to be deleted.", "",
+  &get_unprotect_arguments, &execute_unprotect);
+
+} // namespace snap
+} // namespace action
+} // namespace rbd
diff --git a/src/tools/rbd/action/Status.cc b/src/tools/rbd/action/Status.cc
new file mode 100644 (file)
index 0000000..5cf4b72
--- /dev/null
@@ -0,0 +1,61 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "tools/rbd/ArgumentTypes.h"
+#include "tools/rbd/Shell.h"
+#include "tools/rbd/Utils.h"
+#include "common/errno.h"
+#include "common/Formatter.h"
+#include <iostream>
+#include <boost/program_options.hpp>
+
+namespace rbd {
+namespace action {
+namespace status {
+
+namespace at = argument_types;
+namespace po = boost::program_options;
+
+void get_arguments(po::options_description *positional,
+                   po::options_description *options) {
+  at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
+  at::add_format_options(options);
+}
+
+int execute(const po::variables_map &vm) {
+  size_t arg_index = 0;
+  std::string pool_name;
+  std::string image_name;
+  std::string snap_name;
+  int r = utils::get_pool_image_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &image_name,
+    &snap_name, utils::SNAPSHOT_PRESENCE_NONE);
+  if (r < 0) {
+    return r;
+  }
+
+  at::Format::Formatter formatter;
+  r = utils::get_formatter(vm, &formatter);
+  if (r < 0) {
+    return r;
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  librbd::Image image;
+  r = utils::init_and_open_image(pool_name, image_name, "", true, &rados,
+                                 &io_ctx, &image);
+  if (r < 0) {
+    return r;
+  }
+
+  return 0;
+}
+
+Shell::Action action(
+  {"status"}, {}, "Show the status of this image.", "", &get_arguments,
+  &execute);
+
+} // namespace status
+} // namespace action
+} // namespace rbd
diff --git a/src/tools/rbd/action/Watch.cc b/src/tools/rbd/action/Watch.cc
new file mode 100644 (file)
index 0000000..1570ca8
--- /dev/null
@@ -0,0 +1,83 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "tools/rbd/ArgumentTypes.h"
+#include "tools/rbd/Shell.h"
+#include "tools/rbd/Utils.h"
+#include "common/errno.h"
+#include <iostream>
+#include <boost/program_options.hpp>
+
+namespace rbd {
+namespace action {
+namespace watch {
+
+namespace at = argument_types;
+namespace po = boost::program_options;
+
+class RbdWatchCtx : public librados::WatchCtx2 {
+public:
+  RbdWatchCtx(librados::IoCtx& io_ctx, const char *image_name,
+              std::string header_oid)
+    : m_io_ctx(io_ctx), m_image_name(image_name), m_header_oid(header_oid)
+  {
+  }
+
+  virtual ~RbdWatchCtx() {}
+
+  virtual void handle_notify(uint64_t notify_id,
+                             uint64_t cookie,
+                             uint64_t notifier_id,
+                             bufferlist& bl) {
+    std::cout << m_image_name << " received notification: notify_id="
+              << notify_id << ", cookie=" << cookie << ", notifier_id="
+              << notifier_id << ", bl.length=" << bl.length() << std::endl;
+    bufferlist reply;
+    m_io_ctx.notify_ack(m_header_oid, notify_id, cookie, reply);
+  }
+
+  virtual void handle_error(uint64_t cookie, int err) {
+    std::cerr << m_image_name << " received error: cookie=" << cookie << ", "
+              << "err=" << cpp_strerror(err) << std::endl;
+  }
+private:
+  librados::IoCtx m_io_ctx;
+  const char *m_image_name;
+  std::string m_header_oid;
+};
+
+void get_arguments(po::options_description *positional,
+                   po::options_description *options) {
+  at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
+}
+
+int execute(const po::variables_map &vm) {
+  size_t arg_index = 0;
+  std::string pool_name;
+  std::string image_name;
+  std::string snap_name;
+  int r = utils::get_pool_image_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &image_name,
+    &snap_name, utils::SNAPSHOT_PRESENCE_NONE);
+  if (r < 0) {
+    return r;
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  librbd::Image image;
+  r = utils::init_and_open_image(pool_name, image_name, "", true, &rados,
+                                 &io_ctx, &image);
+  if (r < 0) {
+    return r;
+  }
+
+  return 0;
+}
+
+Shell::Action action(
+  {"watch"}, {}, "Watch events on image.", "", &get_arguments, &execute);
+
+} // namespace watch
+} // namespace action
+} // namespace rbd