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'
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}
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()
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
}
rbd.cc
ArgumentTypes.cc
IndentStream.cc
+ MirrorDaemonServiceInfo.cc
OptionPrinter.cc
Shell.cc
Utils.cc
--- /dev/null
+// -*- 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
+
--- /dev/null
+// -*- 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
*
*/
#include "tools/rbd/ArgumentTypes.h"
+#include "tools/rbd/MirrorDaemonServiceInfo.h"
#include "tools/rbd/Shell.h"
#include "tools/rbd/Utils.h"
#include "include/stringify.h"
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));
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);
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;
// 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"
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) {
}
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);
}
}
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));
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 {
<< 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;
}
}
}
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>
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) {