]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
rbd: add new 'mirror pool' rbd commands
authorJason Dillaman <dillaman@redhat.com>
Thu, 24 Sep 2015 18:34:16 +0000 (14:34 -0400)
committerJason Dillaman <dillaman@redhat.com>
Wed, 2 Dec 2015 16:11:37 +0000 (11:11 -0500)
Signed-off-by: Jason Dillaman <dillaman@redhat.com>
src/CMakeLists.txt
src/tools/Makefile-client.am
src/tools/rbd/ArgumentTypes.cc
src/tools/rbd/ArgumentTypes.h
src/tools/rbd/Shell.cc
src/tools/rbd/Utils.cc
src/tools/rbd/Utils.h
src/tools/rbd/action/List.cc
src/tools/rbd/action/MirrorPool.cc [new file with mode: 0644]

index 903a2226f1330e1692a3913c1fd3915a1d2aa572..87b0b90e9b761f94850ab9d9530bd0647ca75001 100644 (file)
@@ -942,6 +942,7 @@ if(${WITH_RBD})
     tools/rbd/action/List.cc
     tools/rbd/action/Lock.cc
     tools/rbd/action/MergeDiff.cc
+    tools/rbd/action/MirrorPool.cc
     tools/rbd/action/ObjectMap.cc
     tools/rbd/action/Remove.cc
     tools/rbd/action/Rename.cc
index 3935c44ce6a0576fd35a1cd8d4451ab375c9774c..904261bf5b450c2cbfe549555cd6bf0d5d23d7d3 100644 (file)
@@ -50,6 +50,7 @@ rbd_SOURCES = \
        tools/rbd/action/List.cc \
        tools/rbd/action/Lock.cc \
        tools/rbd/action/MergeDiff.cc \
+       tools/rbd/action/MirrorPool.cc \
        tools/rbd/action/ObjectMap.cc \
        tools/rbd/action/Remove.cc \
        tools/rbd/action/Rename.cc \
index f18e88d76a6a5b7fc71182ddfc8fc2e3683dd54e..1618a6e51ba3820e6d7ee72f44195f2328c7c48a 100644 (file)
@@ -124,6 +124,14 @@ void add_snap_option(po::options_description *opt,
     (name.c_str(), po::value<std::string>(), description.c_str());
 }
 
+void add_pool_options(boost::program_options::options_description *pos,
+                      boost::program_options::options_description *opt) {
+  pos->add_options()
+    ("pool-name", "pool name");
+  opt->add_options()
+    ((POOL_NAME + ",p").c_str(), po::value<std::string>(), "pool name");
+}
+
 void add_image_spec_options(po::options_description *pos,
                             po::options_description *opt,
                             ArgumentModifier modifier) {
index 47ad55f107d8e16ecd60c874899b09391b8bf7d2..d8ecadd521c2293c2bea2a2a3d0a2b0d2cc4b1a9 100644 (file)
@@ -43,6 +43,7 @@ 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 CONFIG_PATH("conf");
 static const std::string POOL_NAME("pool");
 static const std::string DEST_POOL_NAME("dest-pool");
 static const std::string IMAGE_NAME("image");
@@ -107,6 +108,9 @@ void add_image_option(boost::program_options::options_description *opt,
 void add_snap_option(boost::program_options::options_description *opt,
                      ArgumentModifier modifier);
 
+void add_pool_options(boost::program_options::options_description *pos,
+                      boost::program_options::options_description *opt);
+
 void add_image_spec_options(boost::program_options::options_description *pos,
                             boost::program_options::options_description *opt,
                             ArgumentModifier modifier);
index 3e2987bd7e06b9a8baab78bb9e30f0837a4ba3a9..57af89a567a647c16fe6b6d0c2f73e8102b488ca 100644 (file)
@@ -234,7 +234,7 @@ Shell::Action *Shell::find_action(const CommandSpec &command_spec,
 
 void Shell::get_global_options(po::options_description *opts) {
   opts->add_options()
-    ("conf,c", po::value<std::string>(), "path to cluster configuration")
+    ((at::CONFIG_PATH + ",c").c_str(), po::value<std::string>(), "path to cluster configuration")
     ("cluster", po::value<std::string>(), "cluster name")
     ("id", po::value<std::string>(), "client id (without 'client.' prefix)")
     ("user", po::value<std::string>(), "client id (without 'client.' prefix)")
index 02af9ef1f885fe4137c0906dd2bf2472e55f1d5c..4a56785e865793341667837f10cdc7fb4fb5eed4 100644 (file)
@@ -114,6 +114,24 @@ std::string get_positional_argument(const po::variables_map &vm, size_t index) {
   return "";
 }
 
+std::string get_pool_name(const po::variables_map &vm,
+                          size_t *arg_index) {
+  std::string pool_name;
+  if (vm.count(at::POOL_NAME)) {
+    pool_name = vm[at::POOL_NAME].as<std::string>();
+  } else {
+    pool_name = get_positional_argument(vm, *arg_index);
+    if (!pool_name.empty()) {
+       ++(*arg_index);
+    }
+  }
+
+  if (pool_name.empty()) {
+    pool_name = at::DEFAULT_POOL_NAME;
+  }
+  return pool_name;
+}
+
 int get_pool_image_snapshot_names(const po::variables_map &vm,
                                   at::ArgumentModifier mod,
                                   size_t *spec_arg_index,
index 0b7794e9fe5be8094095dd2a64355892f59d2e4e..b30236db0be3c51c925c384e59638611287da759 100644 (file)
@@ -46,6 +46,9 @@ int extract_spec(const std::string &spec, std::string *pool_name,
 std::string get_positional_argument(
     const boost::program_options::variables_map &vm, size_t index);
 
+std::string get_pool_name(const boost::program_options::variables_map &vm,
+                          size_t *arg_index);
+
 int get_pool_image_snapshot_names(
     const boost::program_options::variables_map &vm,
     argument_types::ArgumentModifier mod, size_t *spec_arg_index,
index 080d8f419bb6ace288f8f2d499db18e4842d96c8..45ca812a5ae7c2957ba23f1dff2f76c2bdbdb5a9 100644 (file)
@@ -189,23 +189,15 @@ out:
 
 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");
+    ("long,l", po::bool_switch(), "long listing format");
+  at::add_pool_options(positional, options);
   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;
-  }
+  size_t arg_index = 0;
+  std::string pool_name = utils::get_pool_name(vm, &arg_index);
 
   at::Format::Formatter formatter;
   int r = utils::get_formatter(vm, &formatter);
diff --git a/src/tools/rbd/action/MirrorPool.cc b/src/tools/rbd/action/MirrorPool.cc
new file mode 100644 (file)
index 0000000..4d37ec1
--- /dev/null
@@ -0,0 +1,421 @@
+// -*- 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 "include/stringify.h"
+#include "common/config.h"
+#include "common/errno.h"
+#include "common/Formatter.h"
+#include "common/TextTable.h"
+#include "global/global_context.h"
+#include <iostream>
+#include <boost/program_options.hpp>
+#include <boost/regex.hpp>
+
+namespace rbd {
+namespace action {
+namespace mirror_pool {
+
+namespace at = argument_types;
+namespace po = boost::program_options;
+
+namespace {
+
+int init_remote(const std::string &config_path, const std::string &client_name,
+                const std::string &cluster_name, const std::string &pool_name,
+                librados::Rados *rados, librados::IoCtx *io_ctx) {
+  int r = rados->init2(client_name.c_str(), cluster_name.c_str(), 0);
+  if (r < 0) {
+    std::cerr << "rbd: couldn't initialize remote rados!" << std::endl;
+    return r;
+  }
+
+  r = rados->conf_read_file(config_path.empty() ? nullptr :
+                                                  config_path.c_str());
+  if (r < 0) {
+    std::cerr << "rbd: couldn't read remote configuration" << std::endl;
+    return r;
+  }
+
+  r = rados->connect();
+  if (r < 0) {
+    std::cerr << "rbd: couldn't connect to the remote cluster!" << std::endl;
+    return r;
+  }
+
+  if (io_ctx != nullptr) {
+    r = utils::init_io_ctx(*rados, pool_name, io_ctx);
+    if (r < 0) {
+      return r;
+    }
+  }
+  return 0;
+}
+
+int validate_uuid(const std::string &uuid) {
+  boost::regex pattern("^[A-F0-9]{8}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{12}$",
+                       boost::regex::icase);
+  boost::smatch match;
+  if (!boost::regex_match(uuid, match, pattern)) {
+    std::cerr << "rbd: invalid uuid '" << uuid << "'" << std::endl;
+    return -EINVAL;
+  }
+  return 0;
+}
+
+void add_cluster_uuid_option(po::options_description *positional) {
+  positional->add_options()
+    ("cluster-uuid", po::value<std::string>(), "cluster UUID");
+}
+
+int get_cluster_uuid(const po::variables_map &vm, size_t arg_index,
+                     std::string *cluster_uuid) {
+  *cluster_uuid = utils::get_positional_argument(vm, arg_index);
+  if (cluster_uuid->empty()) {
+    std::cerr << "rbd: must specify cluster uuid" << std::endl;
+    return -EINVAL;
+  }
+  return validate_uuid(*cluster_uuid);
+}
+
+int get_remote_cluster_spec(const po::variables_map &vm,
+                            const std::string &spec,
+                            std::string *remote_client_name,
+                            std::string *remote_cluster,
+                            std::string *remote_cluster_uuid) {
+  if (vm.count("remote-client-name")) {
+    *remote_client_name = vm["remote-client-name"].as<std::string>();
+  }
+  if (vm.count("remote-cluster")) {
+    *remote_cluster = vm["remote-cluster"].as<std::string>();
+  }
+  if (vm.count("remote-cluster-uuid")) {
+    *remote_cluster_uuid = vm["remote-cluster-uuid"].as<std::string>();
+    int r = validate_uuid(*remote_cluster_uuid);
+    if (r < 0) {
+      return r;
+    }
+  }
+
+  if (!spec.empty()) {
+    boost::regex pattern("^(?:(client\\.[^@]+)@)?([^/@]+)$");
+    boost::smatch match;
+    if (!boost::regex_match(spec, match, pattern)) {
+      std::cerr << "rbd: invalid spec '" << spec << "'" << std::endl;
+      return -EINVAL;
+    }
+    if (match[1].matched) {
+      *remote_client_name = match[1];
+    }
+    *remote_cluster = match[2];
+  }
+
+  if (remote_cluster->empty()) {
+    std::cerr << "rbd: remote cluster was not specified" << std::endl;
+    return -EINVAL;
+  }
+  return 0;
+}
+
+void format_mirror_peers(const std::string &config_path,
+                         at::Format::Formatter formatter,
+                         const std::vector<librbd::mirror_peer_t> &peers) {
+  if (formatter != nullptr) {
+    formatter->open_array_section("peers");
+    for (auto &peer : peers) {
+      formatter->open_object_section("peer");
+      formatter->dump_string("cluster_uuid", peer.cluster_uuid);
+      formatter->dump_string("cluster_name", peer.cluster_name);
+      formatter->dump_string("client_name", peer.client_name);
+      formatter->close_section();
+    }
+    formatter->close_section();
+  } else {
+    std::cout << "Peers: ";
+    if (peers.empty()) {
+      std::cout << "none" << std::endl;
+    } else {
+      TextTable tbl;
+      tbl.define_column("", TextTable::LEFT, TextTable::LEFT);
+      tbl.define_column("UUID", TextTable::LEFT, TextTable::LEFT);
+      tbl.define_column("NAME", TextTable::LEFT, TextTable::LEFT);
+      tbl.define_column("CLIENT", TextTable::LEFT, TextTable::LEFT);
+      for (auto &peer : peers) {
+        tbl << " "
+            << peer.cluster_uuid
+            << peer.cluster_name
+            << peer.client_name
+            << TextTable::endrow;
+      }
+      std::cout << std::endl << tbl;
+    }
+  }
+}
+
+} // anonymous namespace
+
+void get_peer_add_arguments(po::options_description *positional,
+                            po::options_description *options) {
+  at::add_pool_options(positional, options);
+  positional->add_options()
+    ("remote-cluster-spec", "remote cluster spec\n"
+     "(example: [<client name>@]<cluster name>");
+  options->add_options()
+    ("remote-client-name", po::value<std::string>(), "remote client name")
+    ("remote-cluster", po::value<std::string>(), "remote cluster name")
+    ("remote-cluster-uuid", po::value<std::string>(), "remote cluster uuid");
+}
+
+int execute_peer_add(const po::variables_map &vm) {
+  size_t arg_index = 0;
+  std::string pool_name = utils::get_pool_name(vm, &arg_index);
+
+  std::string remote_client_name = g_ceph_context->_conf->name.to_str();
+  std::string remote_cluster;
+  std::string remote_cluster_uuid;
+  int r = get_remote_cluster_spec(
+    vm, utils::get_positional_argument(vm, arg_index),
+    &remote_client_name, &remote_cluster, &remote_cluster_uuid);
+  if (r < 0) {
+    return r;
+  }
+
+  std::string config_path;
+  if (vm.count(at::CONFIG_PATH)) {
+    config_path = vm[at::CONFIG_PATH].as<std::string>();
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  r = utils::init(pool_name, &rados, &io_ctx);
+  if (r < 0) {
+    return r;
+  }
+
+  if (remote_cluster_uuid.empty()) {
+    librados::Rados remote_rados;
+    librados::IoCtx remote_io_ctx;
+    r = init_remote(config_path, remote_client_name, remote_cluster,
+                    pool_name, &remote_rados, &remote_io_ctx);
+    if (r < 0) {
+      return r;
+    }
+
+    r = remote_rados.cluster_fsid(&remote_cluster_uuid);
+    if (r < 0) {
+      std::cerr << "rbd: error retrieving remote cluster id" << std::endl;
+      return r;
+    }
+  }
+
+  librbd::RBD rbd;
+  r = rbd.mirror_peer_add(io_ctx, remote_cluster_uuid, remote_cluster,
+                          remote_client_name);
+  if (r < 0) {
+    std::cerr << "rbd: error adding mirror peer" << std::endl;
+    return r;
+  }
+  return 0;
+}
+
+void get_peer_remove_arguments(po::options_description *positional,
+                               po::options_description *options) {
+  at::add_pool_options(positional, options);
+  add_cluster_uuid_option(positional);
+}
+
+int execute_peer_remove(const po::variables_map &vm) {
+  size_t arg_index = 0;
+  std::string pool_name = utils::get_pool_name(vm, &arg_index);
+
+  std::string cluster_uuid;
+  int r = get_cluster_uuid(vm, arg_index, &cluster_uuid);
+  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;
+  }
+
+  librbd::RBD rbd;
+  r = rbd.mirror_peer_remove(io_ctx, cluster_uuid);
+  if (r < 0) {
+    std::cerr << "rbd: error removing mirror peer" << std::endl;
+    return r;
+  }
+  return 0;
+}
+
+void get_peer_set_arguments(po::options_description *positional,
+                            po::options_description *options) {
+  at::add_pool_options(positional, options);
+  add_cluster_uuid_option(positional);
+  positional->add_options()
+    ("key", "peer parameter [client or cluster]")
+    ("value", "new client or cluster name");
+}
+
+int execute_peer_set(const po::variables_map &vm) {
+  size_t arg_index = 0;
+  std::string pool_name = utils::get_pool_name(vm, &arg_index);
+
+  std::string cluster_uuid;
+  int r = get_cluster_uuid(vm, arg_index++, &cluster_uuid);
+  if (r < 0) {
+    return r;
+  }
+
+  std::string key = utils::get_positional_argument(vm, arg_index++);
+  if (key != "client" && key != "cluster") {
+    std::cerr << "rbd: must specify 'client' or 'cluster' key." << std::endl;
+    return -EINVAL;
+  }
+
+  std::string value = utils::get_positional_argument(vm, arg_index++);
+  if (value.empty()) {
+    std::cerr << "rbd: must specify new " << key << " value." << std::endl;
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  r = utils::init(pool_name, &rados, &io_ctx);
+  if (r < 0) {
+    return r;
+  }
+
+  librbd::RBD rbd;
+  if (key == "client") {
+    r = rbd.mirror_peer_set_client(io_ctx, cluster_uuid.c_str(), value.c_str());
+  } else {
+    r = rbd.mirror_peer_set_cluster(io_ctx, cluster_uuid.c_str(),
+                                    value.c_str());
+  }
+  if (r < 0) {
+    return r;
+  }
+  return 0;
+}
+
+void get_enable_disable_arguments(po::options_description *positional,
+                                  po::options_description *options) {
+  at::add_pool_options(positional, options);
+}
+
+int execute_enable_disable(const po::variables_map &vm, bool enabled) {
+  size_t arg_index = 0;
+  std::string pool_name = utils::get_pool_name(vm, &arg_index);
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  int r = utils::init(pool_name, &rados, &io_ctx);
+  if (r < 0) {
+    return r;
+  }
+
+  librbd::RBD rbd;
+  r = rbd.mirror_set_enabled(io_ctx, enabled);
+  if (r < 0) {
+    return r;
+  }
+  return 0;
+}
+
+int execute_disable(const po::variables_map &vm) {
+  return execute_enable_disable(vm, false);
+}
+
+int execute_enable(const po::variables_map &vm) {
+  return execute_enable_disable(vm, true);
+}
+
+void get_info_arguments(po::options_description *positional,
+                        po::options_description *options) {
+  at::add_pool_options(positional, options);
+  at::add_format_options(options);
+}
+
+int execute_info(const po::variables_map &vm) {
+  size_t arg_index = 0;
+  std::string pool_name = utils::get_pool_name(vm, &arg_index);
+
+  at::Format::Formatter formatter;
+  int r = utils::get_formatter(vm, &formatter);
+  if (r < 0) {
+    return r;
+  }
+
+  std::string config_path;
+  if (vm.count(at::CONFIG_PATH)) {
+    config_path = vm[at::CONFIG_PATH].as<std::string>();
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  r = utils::init(pool_name, &rados, &io_ctx);
+  if (r < 0) {
+    return r;
+  }
+
+  librbd::RBD rbd;
+  bool enabled;
+  r = rbd.mirror_is_enabled(io_ctx, &enabled);
+  if (r < 0) {
+    return r;
+  }
+
+  std::vector<librbd::mirror_peer_t> mirror_peers;
+  r = rbd.mirror_peer_list(io_ctx, &mirror_peers);
+  if (r < 0) {
+    return r;
+  }
+
+  if (formatter != nullptr) {
+    formatter->open_object_section("mirror");
+    formatter->dump_bool("enabled", enabled);
+  } else {
+    std::cout << "Enabled: " << (enabled ? "true" : "false") << std::endl;
+  }
+
+  format_mirror_peers(config_path, formatter, mirror_peers);
+  if (formatter != nullptr) {
+    formatter->close_section();
+    formatter->flush(std::cout);
+  }
+  return 0;
+}
+
+Shell::Action action_add(
+  {"mirror", "pool", "peer", "add"}, {},
+  "Add a mirroring peer to a pool.", "",
+  &get_peer_add_arguments, &execute_peer_add);
+Shell::Action action_remove(
+  {"mirror", "pool", "peer", "remove"}, {},
+  "Remove a mirroring peer from a pool.", "",
+  &get_peer_remove_arguments, &execute_peer_remove);
+Shell::Action action_set(
+  {"mirror", "pool", "peer", "set"}, {},
+  "Update mirroring peer settings.", "",
+  &get_peer_set_arguments, &execute_peer_set);
+
+Shell::Action action_disable(
+  {"mirror", "pool", "disable"}, {},
+  "Disable RBD mirroring by default within a pool.", "",
+  &get_enable_disable_arguments, &execute_disable);
+Shell::Action action_enable(
+  {"mirror", "pool", "enable"}, {},
+  "Enable RBD mirroring by default within a pool.", "",
+  &get_enable_disable_arguments, &execute_enable);
+Shell::Action action_info(
+  {"mirror", "pool", "info"}, {},
+  "Show information about the pool mirroring configuration.", {},
+  &get_info_arguments, &execute_info);
+
+} // namespace mirror_pool
+} // namespace action
+} // namespace rbd