From 7e6fd072c994c2f6a8b82e52b661e09e50de73b2 Mon Sep 17 00:00:00 2001 From: Jason Dillaman Date: Thu, 24 Sep 2015 14:34:16 -0400 Subject: [PATCH] rbd: add new 'mirror pool' rbd commands Signed-off-by: Jason Dillaman --- src/CMakeLists.txt | 1 + src/tools/Makefile-client.am | 1 + src/tools/rbd/ArgumentTypes.cc | 8 + src/tools/rbd/ArgumentTypes.h | 4 + src/tools/rbd/Shell.cc | 2 +- src/tools/rbd/Utils.cc | 18 ++ src/tools/rbd/Utils.h | 3 + src/tools/rbd/action/List.cc | 16 +- src/tools/rbd/action/MirrorPool.cc | 421 +++++++++++++++++++++++++++++ 9 files changed, 461 insertions(+), 13 deletions(-) create mode 100644 src/tools/rbd/action/MirrorPool.cc diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 903a2226f13..87b0b90e9b7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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 diff --git a/src/tools/Makefile-client.am b/src/tools/Makefile-client.am index 3935c44ce6a..904261bf5b4 100644 --- a/src/tools/Makefile-client.am +++ b/src/tools/Makefile-client.am @@ -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 \ diff --git a/src/tools/rbd/ArgumentTypes.cc b/src/tools/rbd/ArgumentTypes.cc index f18e88d76a6..1618a6e51ba 100644 --- a/src/tools/rbd/ArgumentTypes.cc +++ b/src/tools/rbd/ArgumentTypes.cc @@ -124,6 +124,14 @@ void add_snap_option(po::options_description *opt, (name.c_str(), po::value(), 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(), "pool name"); +} + void add_image_spec_options(po::options_description *pos, po::options_description *opt, ArgumentModifier modifier) { diff --git a/src/tools/rbd/ArgumentTypes.h b/src/tools/rbd/ArgumentTypes.h index 47ad55f107d..d8ecadd521c 100644 --- a/src/tools/rbd/ArgumentTypes.h +++ b/src/tools/rbd/ArgumentTypes.h @@ -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); diff --git a/src/tools/rbd/Shell.cc b/src/tools/rbd/Shell.cc index 3e2987bd7e0..57af89a567a 100644 --- a/src/tools/rbd/Shell.cc +++ b/src/tools/rbd/Shell.cc @@ -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(), "path to cluster configuration") + ((at::CONFIG_PATH + ",c").c_str(), po::value(), "path to cluster configuration") ("cluster", po::value(), "cluster name") ("id", po::value(), "client id (without 'client.' prefix)") ("user", po::value(), "client id (without 'client.' prefix)") diff --git a/src/tools/rbd/Utils.cc b/src/tools/rbd/Utils.cc index 02af9ef1f88..4a56785e865 100644 --- a/src/tools/rbd/Utils.cc +++ b/src/tools/rbd/Utils.cc @@ -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(); + } 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, diff --git a/src/tools/rbd/Utils.h b/src/tools/rbd/Utils.h index 0b7794e9fe5..b30236db0be 100644 --- a/src/tools/rbd/Utils.h +++ b/src/tools/rbd/Utils.h @@ -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, diff --git a/src/tools/rbd/action/List.cc b/src/tools/rbd/action/List.cc index 080d8f419bb..45ca812a5ae 100644 --- a/src/tools/rbd/action/List.cc +++ b/src/tools/rbd/action/List.cc @@ -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(), "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(); - } - - 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 index 00000000000..4d37ec129f0 --- /dev/null +++ b/src/tools/rbd/action/MirrorPool.cc @@ -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 +#include +#include + +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(), "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(); + } + if (vm.count("remote-cluster")) { + *remote_cluster = vm["remote-cluster"].as(); + } + if (vm.count("remote-cluster-uuid")) { + *remote_cluster_uuid = vm["remote-cluster-uuid"].as(); + 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 &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: [@]"); + options->add_options() + ("remote-client-name", po::value(), "remote client name") + ("remote-cluster", po::value(), "remote cluster name") + ("remote-cluster-uuid", po::value(), "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(); + } + + 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(); + } + + 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 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 -- 2.39.5