From 58bddd62126b414bbe15af93b3473be799d1cb81 Mon Sep 17 00:00:00 2001 From: Jason Dillaman Date: Tue, 11 Sep 2018 10:21:12 -0400 Subject: [PATCH] rbd: mirror pool peer commands now support mon_host/key overrides Signed-off-by: Jason Dillaman --- src/test/cli/rbd/help.t | 11 +- src/tools/rbd/action/MirrorPool.cc | 316 +++++++++++++++++++++++++---- 2 files changed, 280 insertions(+), 47 deletions(-) diff --git a/src/test/cli/rbd/help.t b/src/test/cli/rbd/help.t index bbbbcaf6751fd..e3a78de7ff5ba 100644 --- a/src/test/cli/rbd/help.t +++ b/src/test/cli/rbd/help.t @@ -1459,7 +1459,7 @@ rbd help mirror pool info usage: rbd mirror pool info [--pool ] [--format ] - [--pretty-format] + [--pretty-format] [--all] Show information about the pool mirroring configuration. @@ -1471,11 +1471,14 @@ -p [ --pool ] arg pool name --format arg output format (plain, json, or xml) [default: plain] --pretty-format pretty formatting (json and xml) + --all list all attributes rbd help mirror pool peer add usage: rbd mirror pool peer add [--pool ] [--remote-client-name ] [--remote-cluster ] + [--remote-mon-host ] + [--remote-key-file ] Add a mirroring peer to a pool. @@ -1489,6 +1492,8 @@ -p [ --pool ] arg pool name --remote-client-name arg remote client name --remote-cluster arg remote cluster name + --remote-mon-host arg remote mon host(s) + --remote-key-file arg path to file containing remote key rbd help mirror pool peer remove usage: rbd mirror pool peer remove [--pool ] @@ -1512,8 +1517,8 @@ Positional arguments pool name peer uuid - peer parameter [client or cluster] - new client or cluster name + peer parameter [client, cluster, mon-host, key-file] + new value for specified key Optional arguments -p [ --pool ] arg pool name diff --git a/src/tools/rbd/action/MirrorPool.cc b/src/tools/rbd/action/MirrorPool.cc index 6f4443d140caf..80b3c960f159a 100644 --- a/src/tools/rbd/action/MirrorPool.cc +++ b/src/tools/rbd/action/MirrorPool.cc @@ -7,6 +7,7 @@ #include "include/Context.h" #include "include/stringify.h" #include "include/rbd/librbd.hpp" +#include "common/ceph_json.h" #include "common/config.h" #include "common/debug.h" #include "common/errno.h" @@ -14,9 +15,11 @@ #include "common/TextTable.h" #include "common/Throttle.h" #include "global/global_context.h" +#include #include #include #include +#include #include #include "include/assert.h" @@ -34,6 +37,8 @@ namespace mirror_pool { namespace at = argument_types; namespace po = boost::program_options; +static const std::string ALL_NAME("all"); + namespace { int validate_mirroring_enabled(librados::IoCtx& io_ctx) { @@ -64,6 +69,24 @@ int validate_uuid(const std::string &uuid) { return 0; } +int read_key_file(std::string path, std::string* key) { + std::ifstream key_file; + key_file.open(path); + if (key_file.fail()) { + std::cerr << "rbd: failed to open " << path << std::endl; + return -EINVAL; + } + + std::getline(key_file, *key); + if (key_file.bad()) { + std::cerr << "rbd: failed to read key from " << path << std::endl; + return -EINVAL; + } + + key_file.close(); + return 0; +} + void add_uuid_option(po::options_description *positional) { positional->add_options() ("uuid", po::value(), "peer uuid"); @@ -82,13 +105,25 @@ int get_uuid(const po::variables_map &vm, size_t arg_index, 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, + std::map* attributes) { 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-mon-host")) { + (*attributes)["mon_host"] = vm["remote-mon-host"].as(); + } + if (vm.count("remote-key-file")) { + std::string key; + int r = read_key_file(vm["remote-key-file"].as(), &key); + if (r < 0) { + return r; + } + (*attributes)["key"] = key; + } if (!spec.empty()) { std::regex pattern("^(?:(client\\.[^@]+)@)?([^/@]+)$"); @@ -110,39 +145,190 @@ int get_remote_cluster_spec(const po::variables_map &vm, return 0; } -void format_mirror_peers(const std::string &config_path, - at::Format::Formatter formatter, - const std::vector &peers) { +std::string get_peer_config_key_name(int64_t pool_id, + const std::string& peer_uuid) { + return RBD_MIRROR_PEER_CONFIG_KEY_PREFIX + stringify(pool_id) + "/" + + peer_uuid; +} + +int remove_peer_config_key(librados::Rados& rados, int64_t pool_id, + const std::string& peer_uuid) { + std::string cmd = + "{" + "\"prefix\": \"config-key rm\", " + "\"key\": \"" + get_peer_config_key_name(pool_id, peer_uuid) + "\"" + "}"; + bufferlist in_bl; + bufferlist out_bl; + int r = rados.mon_command(cmd, in_bl, &out_bl, nullptr); + if (r == -EPERM) { + } else if (r < 0 && r != -ENOENT) { + std::cerr << "rbd: failed to remove mirroring peer config: " + << cpp_strerror(r) << std::endl; + return r; + } + return 0; +} + +int set_peer_config_key(librados::Rados& rados, int64_t pool_id, + const std::string& peer_uuid, + std::map&& attributes) { + std::stringstream ss; + ss << "{"; + for (auto& pair : attributes) { + ss << "\\\"" << pair.first << "\\\": " + << "\\\"" << pair.second << "\\\""; + if (&pair != &(*attributes.rbegin())) { + ss << ", "; + } + } + ss << "}"; + + std::string cmd = + "{" + "\"prefix\": \"config-key set\", " + "\"key\": \"" + get_peer_config_key_name(pool_id, peer_uuid) + "\", " + "\"val\": \"" + ss.str() + "\"" + "}"; + bufferlist in_bl; + bufferlist out_bl; + int r = rados.mon_command(cmd, in_bl, &out_bl, nullptr); + if (r == -EPERM) { + std::cerr << "rbd: permission denied attempting to set peer " + << "config-key secrets in the monitor" << std::endl; + return r; + } else if (r < 0) { + std::cerr << "rbd: failed to update mirroring peer config: " + << cpp_strerror(r) << std::endl; + return r; + } + return 0; +} + +int get_peer_config_key(librados::Rados& rados, int64_t pool_id, + const std::string& peer_uuid, + std::map* attributes) { + std::string cmd = + "{" + "\"prefix\": \"config-key get\", " + "\"key\": \"" + get_peer_config_key_name(pool_id, peer_uuid) + "\"" + "}"; + + bufferlist in_bl; + bufferlist out_bl; + int r = rados.mon_command(cmd, in_bl, &out_bl, nullptr); + if (r == -EPERM) { + std::cerr << "rbd: permission denied attempting to access peer " + << "config-key secrets from the monitor" << std::endl; + return r; + } else if (r == -ENOENT || out_bl.length() == 0) { + return -ENOENT; + } else if (r < 0) { + std::cerr << "rbd: error reading mirroring peer config: " + << cpp_strerror(r) << std::endl; + return r; + } + + bool json_valid = false; + json_spirit::mValue json_root; + if(json_spirit::read(out_bl.to_str(), json_root)) { + try { + auto& json_obj = json_root.get_obj(); + for (auto& pairs : json_obj) { + (*attributes)[pairs.first] = pairs.second.get_str(); + } + json_valid = true; + } catch (std::runtime_error&) { + } + } + + if (!json_valid) { + std::cerr << "rbd: corrupt mirroring peer config" << std::endl; + return -EINVAL; + } + return 0; +} + +int update_peer_config_key(librados::Rados& rados, int64_t pool_id, + const std::string& peer_uuid, + const std::string& key, + const std::string& value) { + std::map attributes; + int r = get_peer_config_key(rados, pool_id, peer_uuid, &attributes); + if (r == -ENOENT) { + return set_peer_config_key(rados, pool_id, peer_uuid, {{key, value}}); + } else if (r < 0) { + return r; + } + + if (value.empty()) { + attributes.erase(key); + } else { + attributes[key] = value; + } + return set_peer_config_key(rados, pool_id, peer_uuid, std::move(attributes)); +} + +int format_mirror_peers(librados::Rados& rados, int64_t pool_id, + at::Format::Formatter formatter, + const std::vector &peers, + bool config_key) { + TextTable tbl; if (formatter != nullptr) { formatter->open_array_section("peers"); - for (auto &peer : peers) { - formatter->open_object_section("peer"); - formatter->dump_string("uuid", peer.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.uuid - << peer.cluster_name - << peer.client_name - << TextTable::endrow; + if (config_key) { + tbl.define_column("MON_HOST", TextTable::LEFT, TextTable::LEFT); + tbl.define_column("KEY", TextTable::LEFT, TextTable::LEFT); } - std::cout << std::endl << tbl; } } + + for (auto &peer : peers) { + std::map attributes; + if (config_key) { + int r = get_peer_config_key(rados, pool_id, peer.uuid, &attributes); + if (r < 0 && r != -ENOENT) { + return r; + } + } + + if (formatter != nullptr) { + formatter->open_object_section("peer"); + formatter->dump_string("uuid", peer.uuid); + formatter->dump_string("cluster_name", peer.cluster_name); + formatter->dump_string("client_name", peer.client_name); + for (auto& pair : attributes) { + formatter->dump_string(pair.first.c_str(), pair.second); + } + formatter->close_section(); + } else { + tbl << " " + << peer.uuid + << peer.cluster_name + << peer.client_name; + if (config_key) { + tbl << attributes["mon_host"] + << attributes["key"]; + } + tbl << TextTable::endrow; + } + } + + if (formatter != nullptr) { + formatter->close_section(); + } else { + std::cout << std::endl << tbl; + } + return 0; } class ImageRequestBase { @@ -524,7 +710,10 @@ void get_peer_add_arguments(po::options_description *positional, "(example: [@])"); options->add_options() ("remote-client-name", po::value(), "remote client name") - ("remote-cluster", po::value(), "remote cluster name"); + ("remote-cluster", po::value(), "remote cluster name") + ("remote-mon-host", po::value(), "remote mon host(s)") + ("remote-key-file", po::value(), + "path to file containing remote key"); } int execute_peer_add(const po::variables_map &vm, @@ -534,18 +723,14 @@ int execute_peer_add(const po::variables_map &vm, std::string remote_client_name = g_ceph_context->_conf->name.to_str(); std::string remote_cluster; + std::map attributes; int r = get_remote_cluster_spec( vm, utils::get_positional_argument(vm, arg_index), - &remote_client_name, &remote_cluster); + &remote_client_name, &remote_cluster, &attributes); if (r < 0) { return r; } - std::string config_path; - if (vm.count(at::CONFIG_PATH)) { - config_path = vm[at::CONFIG_PATH].as(); - } - // TODO support namespaces librados::Rados rados; librados::IoCtx io_ctx; @@ -580,6 +765,14 @@ int execute_peer_add(const po::variables_map &vm, return r; } + if (!attributes.empty()) { + r = set_peer_config_key(rados, io_ctx.get_id(), uuid, + std::move(attributes)); + if (r < 0) { + return r; + } + } + std::cout << uuid << std::endl; return 0; } @@ -614,6 +807,11 @@ int execute_peer_remove(const po::variables_map &vm, return r; } + r = remove_peer_config_key(rados, io_ctx.get_id(), uuid); + if (r < 0) { + return r; + } + librbd::RBD rbd; r = rbd.mirror_peer_remove(io_ctx, uuid); if (r < 0) { @@ -628,8 +826,8 @@ void get_peer_set_arguments(po::options_description *positional, at::add_pool_options(positional, options); add_uuid_option(positional); positional->add_options() - ("key", "peer parameter [client or cluster]") - ("value", "new client or cluster name"); + ("key", "peer parameter [client, cluster, mon-host, key-file]") + ("value", "new value for specified key"); } int execute_peer_set(const po::variables_map &vm, @@ -643,15 +841,32 @@ int execute_peer_set(const po::variables_map &vm, return r; } + std::set valid_keys{{"client", "cluster", "mon-host", + "key-file"}}; 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; + if (valid_keys.find(key) == valid_keys.end()) { + std::cerr << "rbd: must specify "; + for (auto& valid_key : valid_keys) { + std::cerr << "'" << valid_key << "'"; + if (&valid_key != &(*valid_keys.rbegin())) { + std::cerr << ", "; + } + } + std::cerr << " key." << std::endl; return -EINVAL; } std::string value = utils::get_positional_argument(vm, arg_index++); - if (value.empty()) { + if (value.empty() && (key == "client" || key == "cluster")) { std::cerr << "rbd: must specify new " << key << " value." << std::endl; + } else if (key == "key-file") { + key = "key"; + r = read_key_file(value, &value); + if (r < 0) { + return r; + } + } else if (key == "mon-host") { + key = "mon_host"; } // TODO support namespaces @@ -670,8 +885,25 @@ int execute_peer_set(const po::variables_map &vm, librbd::RBD rbd; if (key == "client") { r = rbd.mirror_peer_set_client(io_ctx, uuid.c_str(), value.c_str()); - } else { + } else if (key == "cluster") { r = rbd.mirror_peer_set_cluster(io_ctx, uuid.c_str(), value.c_str()); + } else { + std::vector mirror_peers; + r = rbd.mirror_peer_list(io_ctx, &mirror_peers); + if (r < 0) { + std::cerr << "rbd: failed to list mirror peers" << std::endl; + return r; + } + + if (std::find_if(mirror_peers.begin(), mirror_peers.end(), + [&uuid](const librbd::mirror_peer_t& peer) { + return uuid == peer.uuid; + }) == mirror_peers.end()) { + std::cerr << "rbd: mirror peer " << uuid << " does not exist" + << std::endl; + return -ENOENT; + } + r = update_peer_config_key(rados, io_ctx.get_id(), uuid, key, value); } if (r < 0) { return r; @@ -769,6 +1001,8 @@ void get_info_arguments(po::options_description *positional, po::options_description *options) { at::add_pool_options(positional, options); at::add_format_options(options); + options->add_options() + (ALL_NAME.c_str(), po::bool_switch(), "list all attributes"); } int execute_info(const po::variables_map &vm, @@ -782,11 +1016,6 @@ int execute_info(const po::variables_map &vm, return r; } - std::string config_path; - if (vm.count(at::CONFIG_PATH)) { - config_path = vm[at::CONFIG_PATH].as(); - } - // TODO support namespaces librados::Rados rados; librados::IoCtx io_ctx; @@ -832,7 +1061,11 @@ int execute_info(const po::variables_map &vm, } if (mirror_mode != RBD_MIRROR_MODE_DISABLED) { - format_mirror_peers(config_path, formatter, mirror_peers); + r = format_mirror_peers(rados, io_ctx.get_id(), formatter, mirror_peers, + vm[ALL_NAME].as()); + if (r < 0) { + return r; + } } if (formatter != nullptr) { formatter->close_section(); @@ -861,11 +1094,6 @@ int execute_status(const po::variables_map &vm, bool verbose = vm[at::VERBOSE].as(); - std::string config_path; - if (vm.count(at::CONFIG_PATH)) { - config_path = vm[at::CONFIG_PATH].as(); - } - // TODO support namespaces librados::Rados rados; librados::IoCtx io_ctx; -- 2.39.5