#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"
#include "common/TextTable.h"
#include "common/Throttle.h"
#include "global/global_context.h"
+#include <fstream>
#include <functional>
#include <iostream>
#include <regex>
+#include <set>
#include <boost/program_options.hpp>
#include "include/assert.h"
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) {
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<std::string>(), "peer 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,
+ std::map<std::string, std::string>* attributes) {
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-mon-host")) {
+ (*attributes)["mon_host"] = vm["remote-mon-host"].as<std::string>();
+ }
+ if (vm.count("remote-key-file")) {
+ std::string key;
+ int r = read_key_file(vm["remote-key-file"].as<std::string>(), &key);
+ if (r < 0) {
+ return r;
+ }
+ (*attributes)["key"] = key;
+ }
if (!spec.empty()) {
std::regex pattern("^(?:(client\\.[^@]+)@)?([^/@]+)$");
return 0;
}
-void format_mirror_peers(const std::string &config_path,
- at::Format::Formatter formatter,
- const std::vector<librbd::mirror_peer_t> &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<std::string, std::string>&& 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<std::string, std::string>* 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<std::string, std::string> 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<librbd::mirror_peer_t> &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<std::string, std::string> 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 {
"(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", po::value<std::string>(), "remote cluster name")
+ ("remote-mon-host", po::value<std::string>(), "remote mon host(s)")
+ ("remote-key-file", po::value<std::string>(),
+ "path to file containing remote key");
}
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<std::string, std::string> 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<std::string>();
- }
-
// TODO support namespaces
librados::Rados rados;
librados::IoCtx io_ctx;
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;
}
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) {
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,
return r;
}
+ std::set<std::string> 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
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<librbd::mirror_peer_t> 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;
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,
return r;
}
- std::string config_path;
- if (vm.count(at::CONFIG_PATH)) {
- config_path = vm[at::CONFIG_PATH].as<std::string>();
- }
-
// TODO support namespaces
librados::Rados rados;
librados::IoCtx io_ctx;
}
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<bool>());
+ if (r < 0) {
+ return r;
+ }
}
if (formatter != nullptr) {
formatter->close_section();
bool verbose = vm[at::VERBOSE].as<bool>();
- std::string config_path;
- if (vm.count(at::CONFIG_PATH)) {
- config_path = vm[at::CONFIG_PATH].as<std::string>();
- }
-
// TODO support namespaces
librados::Rados rados;
librados::IoCtx io_ctx;