From 7d2ffd981b2c031527ceedf7e6c3b3b9d2be535c Mon Sep 17 00:00:00 2001 From: Mykola Golub Date: Thu, 25 Oct 2018 13:14:07 +0300 Subject: [PATCH] rbd: show info about mirror instance in image mirror status output It is particularly useful when running multiple rbd-mirror instances in Active-Passive or Active-Active mode. Signed-off-by: Mykola Golub --- qa/workunits/rbd/rbd_mirror_ha.sh | 3 +- qa/workunits/rbd/rbd_mirror_helpers.sh | 26 +++- src/tools/rbd/CMakeLists.txt | 1 + src/tools/rbd/MirrorDaemonServiceInfo.cc | 174 +++++++++++++++++++++++ src/tools/rbd/MirrorDaemonServiceInfo.h | 35 +++++ src/tools/rbd/action/MirrorImage.cc | 28 +++- src/tools/rbd/action/MirrorPool.cc | 60 +++++++- 7 files changed, 312 insertions(+), 15 deletions(-) create mode 100644 src/tools/rbd/MirrorDaemonServiceInfo.cc create mode 100644 src/tools/rbd/MirrorDaemonServiceInfo.h diff --git a/qa/workunits/rbd/rbd_mirror_ha.sh b/qa/workunits/rbd/rbd_mirror_ha.sh index 8f986debcfd13..171ab56980927 100755 --- a/qa/workunits/rbd/rbd_mirror_ha.sh +++ b/qa/workunits/rbd/rbd_mirror_ha.sh @@ -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' diff --git a/qa/workunits/rbd/rbd_mirror_helpers.sh b/qa/workunits/rbd/rbd_mirror_helpers.sh index 41841fabd85bb..f92b575ae8533 100755 --- a/qa/workunits/rbd/rbd_mirror_helpers.sh +++ b/qa/workunits/rbd/rbd_mirror_helpers.sh @@ -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 } diff --git a/src/tools/rbd/CMakeLists.txt b/src/tools/rbd/CMakeLists.txt index e28a21a42da27..e4c8d1f44522b 100644 --- a/src/tools/rbd/CMakeLists.txt +++ b/src/tools/rbd/CMakeLists.txt @@ -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 index 0000000000000..4870c1b22ec6d --- /dev/null +++ b/src/tools/rbd/MirrorDaemonServiceInfo.cc @@ -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 +#include + +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 index 0000000000000..28e9f4230c92f --- /dev/null +++ b/src/tools/rbd/MirrorDaemonServiceInfo.h @@ -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 +#include + +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 m_instance_id_to_service_id; + std::map> m_daemons_metadata; +}; + +} // namespace rbd + +#endif // CEPH_RBD_MIRROR_DAEMON_SERVICE_INFO_H diff --git a/src/tools/rbd/action/MirrorImage.cc b/src/tools/rbd/action/MirrorImage.cc index 046760278a0eb..a250b694c4ff9 100644 --- a/src/tools/rbd/action/MirrorImage.cc +++ b/src/tools/rbd/action/MirrorImage.cc @@ -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; diff --git a/src/tools/rbd/action/MirrorPool.cc b/src/tools/rbd/action/MirrorPool.cc index 825965d9af298..8b2efaded970b 100644 --- a/src/tools/rbd/action/MirrorPool.cc +++ b/src/tools/rbd/action/MirrorPool.cc @@ -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 &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 &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 @@ -1165,7 +1181,37 @@ int execute_status(const po::variables_map &vm, formatter->open_array_section("images"); } - ImageRequestGenerator generator(io_ctx, formatter); + std::map instance_ids; + MirrorDaemonServiceInfo daemon_service_info(io_ctx); + + std::string start_image_id; + while (true) { + std::map 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 generator( + io_ctx, instance_ids, daemon_service_info, formatter); ret = generator.execute(); if (formatter != nullptr) { -- 2.39.5