]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
rbd: show info about mirror instance in image mirror status output 24717/head
authorMykola Golub <mgolub@suse.com>
Thu, 25 Oct 2018 10:14:07 +0000 (13:14 +0300)
committerMykola Golub <mgolub@suse.com>
Mon, 5 Nov 2018 20:34:39 +0000 (22:34 +0200)
It is particularly useful when running multiple rbd-mirror instances
in Active-Passive or Active-Active mode.

Signed-off-by: Mykola Golub <mgolub@suse.com>
qa/workunits/rbd/rbd_mirror_ha.sh
qa/workunits/rbd/rbd_mirror_helpers.sh
src/tools/rbd/CMakeLists.txt
src/tools/rbd/MirrorDaemonServiceInfo.cc [new file with mode: 0644]
src/tools/rbd/MirrorDaemonServiceInfo.h [new file with mode: 0644]
src/tools/rbd/action/MirrorImage.cc
src/tools/rbd/action/MirrorPool.cc

index 8f986debcfd1367159229b31c1ca0412b63faa20..171ab56980927fd6b9aea811b574b7c42c04dc6f 100755 (executable)
@@ -71,7 +71,8 @@ test_replay()
        wait_for_replay_complete ${CLUSTER1}:${LEADER} ${CLUSTER2} ${POOL} \
                                 ${image}
        wait_for_status_in_pool_dir ${CLUSTER1} ${POOL} ${image} 'up+replaying' \
-                                   'master_position'
+                                    'master_position' \
+                                    "${MIRROR_USER_ID_PREFIX}${LEADER} on $(hostname -s)"
        if [ -z "${RBD_MIRROR_USE_RBD_MIRROR}" ]; then
            wait_for_status_in_pool_dir ${CLUSTER2} ${POOL} ${image} \
                                        'down+unknown'
index 41841fabd85bb3c489039ea78a0cefaa1689d998..f92b575ae85338c0cdcc0c1e57286227cc2189f8 100755 (executable)
@@ -462,6 +462,8 @@ status()
     do
        echo "${cluster} status"
        ceph --cluster ${cluster} -s
+       ceph --cluster ${cluster} service dump
+       ceph --cluster ${cluster} service status
        echo
 
        for image_pool in ${POOL} ${PARENT_POOL}
@@ -673,14 +675,25 @@ test_status_in_pool_dir()
     local cluster=$1
     local pool=$2
     local image=$3
-    local state_pattern=$4
-    local description_pattern=$5
+    local state_pattern="$4"
+    local description_pattern="$5"
+    local service_pattern="$6"
 
     local status_log=${TEMPDIR}/${cluster}-${image}.mirror_status
     rbd --cluster ${cluster} -p ${pool} mirror image status ${image} |
        tee ${status_log} >&2
     grep "state: .*${state_pattern}" ${status_log} || return 1
     grep "description: .*${description_pattern}" ${status_log} || return 1
+
+    if [ -n "${service_pattern}" ]; then
+        grep "service: *${service_pattern}" ${status_log} || return 1
+    elif echo ${state_pattern} | grep '^up+'; then
+        grep "service: *${MIRROR_USER_ID_PREFIX}.* on " ${status_log} || return 1
+    else
+        grep "service: " ${status_log} && return 1
+    fi
+
+    return 0
 }
 
 wait_for_status_in_pool_dir()
@@ -688,12 +701,15 @@ wait_for_status_in_pool_dir()
     local cluster=$1
     local pool=$2
     local image=$3
-    local state_pattern=$4
-    local description_pattern=$5
+    local state_pattern="$4"
+    local description_pattern="$5"
+    local service_pattern="$6"
 
     for s in 1 2 4 8 8 8 8 8 8 8 8 16 16; do
        sleep ${s}
-       test_status_in_pool_dir ${cluster} ${pool} ${image} ${state_pattern} ${description_pattern} && return 0
+       test_status_in_pool_dir ${cluster} ${pool} ${image} "${state_pattern}" \
+                                "${description_pattern}" "${service_pattern}" &&
+            return 0
     done
     return 1
 }
index e28a21a42da2726c1b20feb56a3476ab04dbb78f..e4c8d1f44522bfcb413746283fa404f746903f6b 100644 (file)
@@ -2,6 +2,7 @@ set(rbd_srcs
   rbd.cc
   ArgumentTypes.cc
   IndentStream.cc
+  MirrorDaemonServiceInfo.cc
   OptionPrinter.cc
   Shell.cc
   Utils.cc
diff --git a/src/tools/rbd/MirrorDaemonServiceInfo.cc b/src/tools/rbd/MirrorDaemonServiceInfo.cc
new file mode 100644 (file)
index 0000000..4870c1b
--- /dev/null
@@ -0,0 +1,174 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "common/ceph_json.h"
+#include "common/errno.h"
+#include "include/rados/librados.hpp"
+#include "include/stringify.h"
+#include "tools/rbd/MirrorDaemonServiceInfo.h"
+
+#include <boost/scope_exit.hpp>
+#include <iostream>
+
+namespace rbd {
+
+int MirrorDaemonServiceInfo::init() {
+
+  std::string cmd = "{\"prefix\": \"service dump\"}";
+
+  bufferlist in_bl;
+  bufferlist out_bl;
+  int r = librados::Rados(m_io_ctx).mgr_command(cmd, in_bl, &out_bl, nullptr);
+  if (r < 0) {
+    std::cerr << "rbd: failed to get service dump: " << 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();
+      if (json_obj.count("services")) {
+        auto &services = json_obj["services"].get_obj();
+        if (services.count("rbd-mirror")) {
+          auto &mirror_service = services["rbd-mirror"].get_obj();
+          if (mirror_service.count("daemons")) {
+            for (auto &it : mirror_service["daemons"].get_obj()) {
+              if (it.second.type() != json_spirit::obj_type ||
+                  !it.second.get_obj().count("metadata")) {
+                continue;
+              }
+              auto &service_id = it.first;
+              auto &daemon_metadata = it.second.get_obj()["metadata"].get_obj();
+              for (auto &iter : daemon_metadata) {
+                if (iter.second.type() != json_spirit::str_type) {
+                  continue;
+                }
+                m_daemons_metadata[service_id][iter.first] = iter.second.get_str();
+              }
+            }
+          }
+        }
+      }
+      json_valid = true;
+    } catch (std::runtime_error&) {
+    }
+  }
+
+  if (!json_valid) {
+    std::cerr << "rbd: failed to parse service status" << std::endl;
+    return -EBADMSG;
+  }
+
+  cmd = "{\"prefix\": \"service status\"}";
+
+  out_bl.clear();
+  r = librados::Rados(m_io_ctx).mgr_command(cmd, in_bl, &out_bl, nullptr);
+  if (r < 0) {
+    std::cerr << "rbd: failed to get service status: " << cpp_strerror(r)
+              << std::endl;
+    return r;
+  }
+
+  json_valid = false;
+  if (json_spirit::read(out_bl.to_str(), json_root)) {
+    try {
+      auto& json_obj = json_root.get_obj();
+      if (json_obj.count("rbd-mirror")) {
+        auto &mirror_service = json_obj["rbd-mirror"].get_obj();
+        for (auto &it : mirror_service) {
+          auto &service_id = it.first;
+          auto &daemon = it.second.get_obj();
+          if (daemon.count("status") &&
+              daemon["status"].get_obj().count("json")) {
+            auto& status_json_str =
+                daemon["status"].get_obj()["json"].get_str();
+            json_spirit::mValue status_json_root;
+            if (json_spirit::read(status_json_str, status_json_root)) {
+              auto& status = status_json_root.get_obj();
+              auto iter = status.find(stringify(m_io_ctx.get_id()));
+              if (iter != status.end() && 
+                  iter->second.get_obj().count("instance_id")) {
+                auto &instance_id =
+                  iter->second.get_obj()["instance_id"].get_str();
+                m_instance_id_to_service_id[instance_id] = service_id;
+              }
+            }
+          }
+        }
+      }
+      json_valid = true;
+    } catch (std::runtime_error&) {
+    }
+  }
+
+  if (!json_valid) {
+    std::cerr << "rbd: failed to parse service status" << std::endl;
+    return -EBADMSG;
+  }
+
+  return 0;
+}
+
+std::string MirrorDaemonServiceInfo::get_description(
+    const std::string &instance_id) const {
+  if (!m_instance_id_to_service_id.count(instance_id)) {
+    return {};
+  }
+
+  auto service_id = m_instance_id_to_service_id.find(instance_id)->second;
+
+  auto it = m_daemons_metadata.find(service_id);
+  if (it == m_daemons_metadata.end()) {
+    return service_id;
+  }
+
+  auto &metadata = it->second;
+  auto iter = metadata.find("id");
+  std::string description = (iter != metadata.end()) ?
+      iter->second : service_id;
+  iter = metadata.find("hostname");
+  if (iter != metadata.end()) {
+    description += " on " + iter->second;
+  }
+
+  return description;
+}
+
+void MirrorDaemonServiceInfo::dump(
+    const std::string &instance_id,
+    argument_types::Format::Formatter formatter) const {
+  formatter->open_object_section("daemon_service");
+  BOOST_SCOPE_EXIT(formatter) {
+    formatter->close_section();
+  } BOOST_SCOPE_EXIT_END;
+
+  if (instance_id.empty() ||
+      !m_instance_id_to_service_id.count(instance_id)) {
+    return;
+  }
+
+  auto service_id = m_instance_id_to_service_id.find(instance_id)->second;
+  formatter->dump_string("service_id", service_id);
+  formatter->dump_string("instance_id", instance_id);
+
+  auto it = m_daemons_metadata.find(service_id);
+  if (it == m_daemons_metadata.end()) {
+    return;
+  }
+
+  auto &metadata = it->second;
+  auto iter = metadata.find("id");
+  if (iter != metadata.end()) {
+    formatter->dump_string("daemon_id", iter->second);
+  }
+  iter = metadata.find("hostname");
+  if (iter != metadata.end()) {
+    formatter->dump_string("hostname", iter->second);
+  }
+}
+
+} // namespace rbd
+
diff --git a/src/tools/rbd/MirrorDaemonServiceInfo.h b/src/tools/rbd/MirrorDaemonServiceInfo.h
new file mode 100644 (file)
index 0000000..28e9f42
--- /dev/null
@@ -0,0 +1,35 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_RBD_MIRROR_DAEMON_SERVICE_INFO_H
+#define CEPH_RBD_MIRROR_DAEMON_SERVICE_INFO_H
+
+#include "tools/rbd/ArgumentTypes.h"
+
+#include <string>
+#include <map>
+
+namespace librados { class IoCtx; }
+
+namespace rbd {
+
+class MirrorDaemonServiceInfo {
+public:
+  MirrorDaemonServiceInfo(librados::IoCtx &io_ctx) : m_io_ctx(io_ctx) {
+  }
+
+  int init();
+
+  std::string get_description(const std::string &instance_id) const;
+  void dump(const std::string &instance_id,
+            argument_types::Format::Formatter formatter) const;
+
+private:
+  librados::IoCtx &m_io_ctx;
+  std::map<std::string, std::string> m_instance_id_to_service_id;
+  std::map<std::string, std::map<std::string, std::string>> m_daemons_metadata;
+};
+
+} // namespace rbd
+
+#endif // CEPH_RBD_MIRROR_DAEMON_SERVICE_INFO_H
index 046760278a0eb53664d1294a2271a05e7f22a951..a250b694c4ff9cc7bdf93cd7559d3cbc031fcdfb 100644 (file)
@@ -12,6 +12,7 @@
  *
  */
 #include "tools/rbd/ArgumentTypes.h"
+#include "tools/rbd/MirrorDaemonServiceInfo.h"
 #include "tools/rbd/Shell.h"
 #include "tools/rbd/Utils.h"
 #include "include/stringify.h"
@@ -282,6 +283,24 @@ int execute_status(const po::variables_map &vm,
     return r;
   }
 
+  std::string instance_id;
+  MirrorDaemonServiceInfo daemon_service_info(io_ctx);
+
+  if (status.up) {
+    r = image.mirror_image_get_instance_id(&instance_id);
+    if (r == -EOPNOTSUPP) {
+      std::cerr << "rbd: newer release of Ceph OSDs required to map image "
+                << "to rbd-mirror daemon instance" << std::endl;
+      // not fatal
+    } else if (r < 0 && r != -ENOENT) {
+      std::cerr << "rbd: failed to get service id for image "
+                << image_name << ": " << cpp_strerror(r) << std::endl;
+      // not fatal
+    } else if (!instance_id.empty()) {
+      daemon_service_info.init();
+    }
+  }
+
   std::string state = utils::mirror_image_status_state(status);
   std::string last_update = (
     status.last_update == 0 ? "" : utils::timestr(status.last_update));
@@ -292,6 +311,7 @@ int execute_status(const po::variables_map &vm,
     formatter->dump_string("global_id", status.info.global_id);
     formatter->dump_string("state", state);
     formatter->dump_string("description", status.description);
+    daemon_service_info.dump(instance_id, formatter);
     formatter->dump_string("last_update", last_update);
     formatter->close_section(); // image
     formatter->flush(std::cout);
@@ -299,8 +319,12 @@ int execute_status(const po::variables_map &vm,
     std::cout << image_name << ":\n"
              << "  global_id:   " << status.info.global_id << "\n"
              << "  state:       " << state << "\n"
-             << "  description: " << status.description << "\n"
-             << "  last_update: " << last_update << std::endl;
+              << "  description: " << status.description << "\n";
+    if (!instance_id.empty()) {
+      std::cout << "  service:     " <<
+        daemon_service_info.get_description(instance_id) << "\n";
+    }
+    std::cout << "  last_update: " << last_update << std::endl;
   }
 
   return 0;
index 825965d9af29843f9915e301e7368d0bfcce42b7..8b2efaded970b910d04df281b01ada4f11d2f975 100644 (file)
@@ -2,6 +2,7 @@
 // vim: ts=8 sw=2 smarttab
 
 #include "tools/rbd/ArgumentTypes.h"
+#include "tools/rbd/MirrorDaemonServiceInfo.h"
 #include "tools/rbd/Shell.h"
 #include "tools/rbd/Utils.h"
 #include "include/Context.h"
@@ -588,10 +589,14 @@ private:
 
 class StatusImageRequest : public ImageRequestBase {
 public:
-  StatusImageRequest(librados::IoCtx &io_ctx, OrderedThrottle &throttle,
-                     const std::string &image_name,
-                     at::Format::Formatter formatter)
+  StatusImageRequest(
+      librados::IoCtx &io_ctx, OrderedThrottle &throttle,
+      const std::string &image_name,
+      const std::map<std::string, std::string> &instance_ids,
+      const MirrorDaemonServiceInfo &daemon_service_info,
+      at::Format::Formatter formatter)
     : ImageRequestBase(io_ctx, throttle, image_name),
+      m_instance_ids(instance_ids), m_daemon_service_info(daemon_service_info),
       m_formatter(formatter) {
   }
 
@@ -602,6 +607,7 @@ protected:
 
   void execute_action(librbd::Image &image,
                       librbd::RBD::AioCompletion *aio_comp) override {
+    image.get_id(&m_image_id);
     image.aio_mirror_image_get_status(&m_mirror_image_status,
                                       sizeof(m_mirror_image_status), aio_comp);
   }
@@ -612,6 +618,9 @@ protected:
     }
 
     std::string state = utils::mirror_image_status_state(m_mirror_image_status);
+    std::string instance_id = (m_mirror_image_status.up &&
+                               m_instance_ids.count(m_image_id)) ?
+        m_instance_ids.find(m_image_id)->second : "";
     std::string last_update = (
       m_mirror_image_status.last_update == 0 ?
         "" : utils::timestr(m_mirror_image_status.last_update));
@@ -624,6 +633,7 @@ protected:
       m_formatter->dump_string("state", state);
       m_formatter->dump_string("description",
                                m_mirror_image_status.description);
+      m_daemon_service_info.dump(instance_id, m_formatter);
       m_formatter->dump_string("last_update", last_update);
       m_formatter->close_section(); // image
     } else {
@@ -632,8 +642,12 @@ protected:
                 << m_mirror_image_status.info.global_id << "\n"
                << "  state:       " << state << "\n"
                << "  description: "
-                << m_mirror_image_status.description << "\n"
-               << "  last_update: " << last_update << std::endl;
+                << m_mirror_image_status.description << "\n";
+      if (!instance_id.empty()) {
+        std::cout << "  service:     "
+                  << m_daemon_service_info.get_description(instance_id) << "\n";
+      }
+      std::cout << "  last_update: " << last_update << std::endl;
     }
   }
 
@@ -642,9 +656,11 @@ protected:
   }
 
 private:
+  const std::map<std::string, std::string> &m_instance_ids;
+  const MirrorDaemonServiceInfo &m_daemon_service_info;
   at::Format::Formatter m_formatter;
+  std::string m_image_id;
   librbd::mirror_image_status_t m_mirror_image_status;
-
 };
 
 template <typename RequestT>
@@ -1165,7 +1181,37 @@ int execute_status(const po::variables_map &vm,
       formatter->open_array_section("images");
     }
 
-    ImageRequestGenerator<StatusImageRequest> generator(io_ctx, formatter);
+    std::map<std::string, std::string> instance_ids;
+    MirrorDaemonServiceInfo daemon_service_info(io_ctx);
+
+    std::string start_image_id;
+    while (true) {
+      std::map<std::string, std::string> ids;
+      r = rbd.mirror_image_instance_id_list(io_ctx, start_image_id, 1024, &ids);
+      if (r < 0) {
+        if (r == -EOPNOTSUPP) {
+          std::cerr << "rbd: newer release of Ceph OSDs required to map image "
+                    << "to rbd-mirror daemon instance" << std::endl;
+        } else {
+          std::cerr << "rbd: failed to get instance id list: "
+                    << cpp_strerror(r) << std::endl;
+        }
+        // not fatal
+        break;
+      }
+      if (ids.empty()) {
+        break;
+      }
+      instance_ids.insert(ids.begin(), ids.end());
+      start_image_id = ids.rbegin()->first;
+    }
+
+    if (!instance_ids.empty()) {
+      daemon_service_info.init();
+    }
+
+    ImageRequestGenerator<StatusImageRequest> generator(
+        io_ctx, instance_ids, daemon_service_info, formatter);
     ret = generator.execute();
 
     if (formatter != nullptr) {