Skip to content

Commit

Permalink
rbd: show info about mirror instance in image mirror status output
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
trociny committed Oct 30, 2018
1 parent a89f3bf commit 74d4ef8
Show file tree
Hide file tree
Showing 7 changed files with 297 additions and 10 deletions.
3 changes: 2 additions & 1 deletion qa/workunits/rbd/rbd_mirror_ha.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
11 changes: 11 additions & 0 deletions qa/workunits/rbd/rbd_mirror_helpers.sh
Original file line number Diff line number Diff line change
Expand Up @@ -675,12 +675,23 @@ test_status_in_pool_dir()
local image=$3
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()
Expand Down
1 change: 1 addition & 0 deletions src/tools/rbd/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ set(rbd_srcs
rbd.cc
ArgumentTypes.cc
IndentStream.cc
MirrorDaemonServiceInfo.cc
OptionPrinter.cc
Shell.cc
Utils.cc
Expand Down
174 changes: 174 additions & 0 deletions src/tools/rbd/MirrorDaemonServiceInfo.cc
Original file line number Diff line number Diff line change
@@ -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

35 changes: 35 additions & 0 deletions src/tools/rbd/MirrorDaemonServiceInfo.h
Original file line number Diff line number Diff line change
@@ -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
28 changes: 26 additions & 2 deletions src/tools/rbd/action/MirrorImage.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -282,6 +283,20 @@ 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 < 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));
Expand All @@ -292,15 +307,24 @@ 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);
formatter->open_object_section("daemon_service");
if (!instance_id.empty()) {
daemon_service_info.dump(instance_id, formatter);
}
formatter->close_section(); // daemon_service
formatter->dump_string("last_update", last_update);
formatter->close_section(); // image
formatter->flush(std::cout);
} else {
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;
Expand Down

0 comments on commit 74d4ef8

Please sign in to comment.