]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
rbd: mirror pool peer commands now support mon_host/key overrides
authorJason Dillaman <dillaman@redhat.com>
Tue, 11 Sep 2018 14:21:12 +0000 (10:21 -0400)
committerJason Dillaman <dillaman@redhat.com>
Thu, 13 Sep 2018 13:58:33 +0000 (09:58 -0400)
Signed-off-by: Jason Dillaman <dillaman@redhat.com>
src/test/cli/rbd/help.t
src/tools/rbd/action/MirrorPool.cc

index bbbbcaf6751fdff17855d38b5f148b2ab84b3e94..e3a78de7ff5ba66c70907fe44c44b7e508b71a5b 100644 (file)
   
   rbd help mirror pool info
   usage: rbd mirror pool info [--pool <pool>] [--format <format>] 
-                              [--pretty-format] 
+                              [--pretty-format] [--all] 
                               <pool-name> 
   
   Show information about the pool mirroring configuration.
     -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 <pool>] 
                                   [--remote-client-name <remote-client-name>] 
                                   [--remote-cluster <remote-cluster>] 
+                                  [--remote-mon-host <remote-mon-host>] 
+                                  [--remote-key-file <remote-key-file>] 
                                   <pool-name> <remote-cluster-spec> 
   
   Add a mirroring peer to a pool.
     -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 <pool>] 
   Positional arguments
     <pool-name>          pool name
     <uuid>               peer uuid
-    <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
   
   Optional arguments
     -p [ --pool ] arg    pool name
index 6f4443d140caff933cb68643043cab14736ed92b..80b3c960f159a2a2256da7fde8727c8d1ead75f5 100644 (file)
@@ -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"
 #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"
 
@@ -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<std::string>(), "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<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\\.[^@]+)@)?([^/@]+)$");
@@ -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<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 {
@@ -524,7 +710,10 @@ void get_peer_add_arguments(po::options_description *positional,
      "(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,
@@ -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<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;
@@ -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<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
@@ -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<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;
@@ -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<std::string>();
-  }
-
   // 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<bool>());
+    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<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;