diff options
Diffstat (limited to 'src/tools/rbd/MirrorDaemonServiceInfo.cc')
-rw-r--r-- | src/tools/rbd/MirrorDaemonServiceInfo.cc | 307 |
1 files changed, 307 insertions, 0 deletions
diff --git a/src/tools/rbd/MirrorDaemonServiceInfo.cc b/src/tools/rbd/MirrorDaemonServiceInfo.cc new file mode 100644 index 000000000..e7422e66a --- /dev/null +++ b/src/tools/rbd/MirrorDaemonServiceInfo.cc @@ -0,0 +1,307 @@ +// -*- 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> + +#include "json_spirit/json_spirit.h" + +namespace rbd { + +std::ostream& operator<<(std::ostream& os, MirrorHealth mirror_health) { + switch (mirror_health) { + case MIRROR_HEALTH_OK: + os << "OK"; + break; + case MIRROR_HEALTH_UNKNOWN: + os << "UNKNOWN"; + break; + case MIRROR_HEALTH_WARNING: + os << "WARNING"; + break; + case MIRROR_HEALTH_ERROR: + os << "ERROR"; + break; + } + return os; +} + +std::string MirrorService::get_image_description() const { + std::string description = (!client_id.empty() ? client_id : + stringify(service_id)); + if (!hostname.empty()) { + description += " on " + hostname; + } + return description; +} + +void MirrorService::dump_image( + argument_types::Format::Formatter formatter) const { + formatter->open_object_section("daemon_service"); + formatter->dump_string("service_id", service_id); + formatter->dump_string("instance_id", instance_id); + formatter->dump_string("daemon_id", client_id); + formatter->dump_string("hostname", hostname); + formatter->close_section(); +} + +int MirrorDaemonServiceInfo::init() { + int r = get_mirror_service_dump(); + if (r < 0) { + return r; + } else if (m_mirror_services.empty()) { + return 0; + } + + r = get_mirror_service_status(); + if (r < 0) { + return r; + } + + return 0; +} + +const MirrorService* MirrorDaemonServiceInfo::get_by_service_id( + const std::string& service_id) const { + auto it = m_mirror_services.find(service_id); + if (it == m_mirror_services.end()) { + return nullptr; + } + + return &it->second; +} + +const MirrorService* MirrorDaemonServiceInfo::get_by_instance_id( + const std::string& instance_id) const { + auto it = m_instance_to_service_ids.find(instance_id); + if (it == m_instance_to_service_ids.end()) { + return nullptr; + } + + return get_by_service_id(it->second); +} + +MirrorServices MirrorDaemonServiceInfo::get_mirror_services() const { + MirrorServices mirror_services; + for (auto& it : m_mirror_services) { + mirror_services.push_back(it.second); + } + return mirror_services; +} + +int MirrorDaemonServiceInfo::get_mirror_service_dump() { + librados::Rados rados(m_io_ctx); + std::string cmd = R"({"prefix": "service dump", "format": "json"})"; + bufferlist in_bl; + bufferlist out_bl; + + int r = rados.mon_command(cmd, in_bl, &out_bl, nullptr); + if (r < 0) { + std::cerr << "rbd: failed to query services: " << cpp_strerror(r) + << std::endl; + return r; + } + + json_spirit::mValue json_root; + if(!json_spirit::read(out_bl.to_str(), json_root)) { + std::cerr << "rbd: invalid service dump JSON received" << std::endl; + return -EBADMSG; + } + + try { + auto& services = json_root.get_obj()["services"]; + if (services.is_null()) { + std::cerr << "rbd: missing services in service dump JSON" << std::endl; + return -EBADMSG; + } + + auto& service = services.get_obj()["rbd-mirror"]; + if (service.is_null()) { + // no rbd-mirror daemons running + return 0; + } + + auto& daemons = service.get_obj()["daemons"]; + if (daemons.is_null()) { + return 0; + } + + for (auto& daemon_pair : daemons.get_obj()) { + // rbd-mirror instances will always be integers but other objects + // are included + auto& service_id = daemon_pair.first; + if (daemon_pair.second.type() != json_spirit::obj_type) { + continue; + } + + auto& daemon = daemon_pair.second.get_obj(); + auto& metadata_val = daemon["metadata"]; + if (metadata_val.is_null()) { + continue; + } + auto& metadata = metadata_val.get_obj(); + + MirrorService mirror_service{service_id}; + + auto& client_id = metadata["id"]; + if (!client_id.is_null()) { + mirror_service.client_id = client_id.get_str(); + } + + auto& ceph_version = metadata["ceph_version_short"]; + if (!ceph_version.is_null()) { + mirror_service.ceph_version = ceph_version.get_str(); + } + + auto& hostname = metadata["hostname"]; + if (!hostname.is_null()) { + mirror_service.hostname = hostname.get_str(); + } + + m_mirror_services[service_id] = mirror_service; + } + + } catch (std::runtime_error&) { + std::cerr << "rbd: unexpected service dump JSON received" << std::endl; + return -EBADMSG; + } + + return 0; +} + +int MirrorDaemonServiceInfo::get_mirror_service_status() { + librados::Rados rados(m_io_ctx); + std::string cmd = R"({"prefix": "service status", "format": "json"})"; + bufferlist in_bl; + bufferlist out_bl; + + int r = rados.mon_command(cmd, in_bl, &out_bl, nullptr); + if (r < 0) { + std::cerr << "rbd: failed to query service status: " << cpp_strerror(r) + << std::endl; + return r; + } + json_spirit::mValue json_root; + if(!json_spirit::read(out_bl.to_str(), json_root)) { + std::cerr << "rbd: invalid service status JSON received" << std::endl; + return -EBADMSG; + } + + bool found_leader = false; + bool found_pool = false; + + try { + auto& service = json_root.get_obj()["rbd-mirror"]; + if (service.is_null()) { + return 0; + } + + for (auto& daemon_pair : service.get_obj()) { + std::string service_id = daemon_pair.first; + auto it = m_mirror_services.find(service_id); + if (it == m_mirror_services.end()) { + continue; + } + + auto& mirror_service = it->second; + auto& daemon = daemon_pair.second.get_obj(); + auto& status = daemon["status"]; + if (status.is_null()) { + mirror_service.callouts.push_back("not reporting status"); + mirror_service.health = MIRROR_HEALTH_WARNING; + continue; + } + + auto& json = status.get_obj()["json"]; + if (json.is_null()) { + mirror_service.callouts.push_back("not reporting status"); + mirror_service.health = MIRROR_HEALTH_WARNING; + continue; + } + + json_spirit::mValue json_status; + if(!json_spirit::read(json.get_str(), json_status)) { + std::cerr << "rbd: invalid service status daemon status JSON received" + << std::endl; + return -EBADMSG; + } + + auto& pool_val = json_status.get_obj()[stringify(m_io_ctx.get_id())]; + if (pool_val.is_null()) { + mirror_service.callouts.push_back("not reporting status for pool"); + mirror_service.health = MIRROR_HEALTH_WARNING; + continue; + } + + auto& pool = pool_val.get_obj(); + found_pool = true; + + auto& instance_id = pool["instance_id"]; + if (!instance_id.is_null()) { + mirror_service.instance_id = instance_id.get_str(); + m_instance_to_service_ids[mirror_service.instance_id] = service_id; + } + + auto& leader = pool["leader"]; + if (!leader.is_null() && leader.get_bool()) { + mirror_service.leader = true; + found_leader = true; + } + + MirrorHealth mirror_service_health = MIRROR_HEALTH_OK; + auto& callouts = pool["callouts"]; + if (!callouts.is_null()) { + for (auto& callout_pair : callouts.get_obj()) { + auto& callout = callout_pair.second.get_obj(); + auto& level = callout["level"]; + if (level.is_null()) { + continue; + } + + auto& level_str = level.get_str(); + if (mirror_service_health < MIRROR_HEALTH_ERROR && + level_str == "error") { + mirror_service_health = MIRROR_HEALTH_ERROR; + } else if (mirror_service_health < MIRROR_HEALTH_WARNING && + level_str == "warning") { + mirror_service_health = MIRROR_HEALTH_WARNING; + } + + auto& text = callout["text"]; + if (!text.is_null()) { + mirror_service.callouts.push_back(text.get_str()); + } + } + } + mirror_service.health = mirror_service_health; + } + } catch (std::runtime_error&) { + std::cerr << "rbd: unexpected service status JSON received" << std::endl; + return -EBADMSG; + } + + // compute overall daemon health + m_daemon_health = MIRROR_HEALTH_OK; + if (!found_pool) { + // no daemons are reporting status for this pool + m_daemon_health = MIRROR_HEALTH_ERROR; + } else if (!found_leader) { + // no daemons are reporting leader role for this pool + m_daemon_health = MIRROR_HEALTH_WARNING; + } + + for (auto& pair : m_mirror_services) { + m_daemon_health = std::max(m_daemon_health, pair.second.health); + } + + return 0; +} + +} // namespace rbd + |