diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 11:54:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 11:54:28 +0000 |
commit | e6918187568dbd01842d8d1d2c808ce16a894239 (patch) | |
tree | 64f88b554b444a49f656b6c656111a145cbbaa28 /src/crimson/admin/osd_admin.cc | |
parent | Initial commit. (diff) | |
download | ceph-e6918187568dbd01842d8d1d2c808ce16a894239.tar.xz ceph-e6918187568dbd01842d8d1d2c808ce16a894239.zip |
Adding upstream version 18.2.2.upstream/18.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/crimson/admin/osd_admin.cc')
-rw-r--r-- | src/crimson/admin/osd_admin.cc | 574 |
1 files changed, 574 insertions, 0 deletions
diff --git a/src/crimson/admin/osd_admin.cc b/src/crimson/admin/osd_admin.cc new file mode 100644 index 000000000..0436e5184 --- /dev/null +++ b/src/crimson/admin/osd_admin.cc @@ -0,0 +1,574 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "crimson/admin/osd_admin.h" +#include <string> +#include <string_view> + +#include <fmt/format.h> +#include <seastar/core/do_with.hh> +#include <seastar/core/future.hh> +#include <seastar/core/thread.hh> +#include <seastar/core/scollectd_api.hh> + +#include "common/config.h" +#include "crimson/admin/admin_socket.h" +#include "crimson/common/log.h" +#include "crimson/osd/exceptions.h" +#include "crimson/osd/osd.h" +#include "crimson/osd/pg.h" +#include "crimson/osd/shard_services.h" + +namespace { +seastar::logger& logger() +{ + return crimson::get_logger(ceph_subsys_osd); +} +} // namespace + +using namespace std::literals; +using std::string_view; +using std::unique_ptr; +using crimson::osd::OSD; +using crimson::common::local_conf; +using namespace crimson::common; +using ceph::common::cmd_getval; +using ceph::common::cmd_getval_or; + +namespace crimson::admin { + +template <class Hook, class... Args> +std::unique_ptr<AdminSocketHook> make_asok_hook(Args&&... args) +{ + return std::make_unique<Hook>(std::forward<Args>(args)...); +} + +/** + * An OSD admin hook: OSD status + */ +class OsdStatusHook : public AdminSocketHook { +public: + explicit OsdStatusHook(const crimson::osd::OSD& osd) : + AdminSocketHook{"status", "", "OSD status"}, + osd(osd) + {} + seastar::future<tell_result_t> call(const cmdmap_t&, + std::string_view format, + ceph::bufferlist&& input) const final + { + unique_ptr<Formatter> f{Formatter::create(format, "json-pretty", "json-pretty")}; + f->open_object_section("status"); + osd.dump_status(f.get()); + f->close_section(); + return seastar::make_ready_future<tell_result_t>(std::move(f)); + } +private: + const crimson::osd::OSD& osd; +}; +template std::unique_ptr<AdminSocketHook> +make_asok_hook<OsdStatusHook>(const crimson::osd::OSD& osd); + +/** + * An OSD admin hook: send beacon + */ +class SendBeaconHook : public AdminSocketHook { +public: + explicit SendBeaconHook(crimson::osd::OSD& osd) : + AdminSocketHook{"send_beacon", + "", + "send OSD beacon to mon immediately"}, + osd(osd) + {} + seastar::future<tell_result_t> call(const cmdmap_t&, + std::string_view format, + ceph::bufferlist&& input) const final + { + return osd.send_beacon().then([] { + return seastar::make_ready_future<tell_result_t>(); + }); + } +private: + crimson::osd::OSD& osd; +}; +template std::unique_ptr<AdminSocketHook> +make_asok_hook<SendBeaconHook>(crimson::osd::OSD& osd); + +/** + * send the latest pg stats to mgr + */ +class FlushPgStatsHook : public AdminSocketHook { +public: + explicit FlushPgStatsHook(crimson::osd::OSD& osd) : + AdminSocketHook("flush_pg_stats", + "", + "flush pg stats"), + osd{osd} + {} + seastar::future<tell_result_t> call(const cmdmap_t&, + std::string_view format, + ceph::bufferlist&& input) const final + { + uint64_t seq = osd.send_pg_stats(); + unique_ptr<Formatter> f{Formatter::create(format, "json-pretty", "json-pretty")}; + f->dump_unsigned("stat_seq", seq); + return seastar::make_ready_future<tell_result_t>(std::move(f)); + } + +private: + crimson::osd::OSD& osd; +}; +template std::unique_ptr<AdminSocketHook> make_asok_hook<FlushPgStatsHook>(crimson::osd::OSD& osd); + +/// dump the history of PGs' peering state +class DumpPGStateHistory final: public AdminSocketHook { +public: + explicit DumpPGStateHistory(const crimson::osd::PGShardManager &pg_shard_manager) : + AdminSocketHook{"dump_pgstate_history", + "", + "dump history of PGs' peering state"}, + pg_shard_manager{pg_shard_manager} + {} + seastar::future<tell_result_t> call(const cmdmap_t&, + std::string_view format, + ceph::bufferlist&& input) const final + { + std::unique_ptr<Formatter> fref{ + Formatter::create(format, "json-pretty", "json-pretty")}; + Formatter *f = fref.get(); + f->open_object_section("pgstate_history"); + f->open_array_section("pgs"); + return pg_shard_manager.for_each_pg([f](auto &pgid, auto &pg) { + f->open_object_section("pg"); + f->dump_stream("pg") << pgid; + const auto& peering_state = pg->get_peering_state(); + f->dump_string("currently", peering_state.get_current_state()); + peering_state.dump_history(f); + f->close_section(); + }).then([fref=std::move(fref)]() mutable { + fref->close_section(); + fref->close_section(); + return seastar::make_ready_future<tell_result_t>(std::move(fref)); + }); + } + +private: + const crimson::osd::PGShardManager &pg_shard_manager; +}; +template std::unique_ptr<AdminSocketHook> make_asok_hook<DumpPGStateHistory>( + const crimson::osd::PGShardManager &); + +//dump the contents of perfcounters in osd and store +class DumpPerfCountersHook final: public AdminSocketHook { +public: + explicit DumpPerfCountersHook() : + AdminSocketHook{"perfcounters_dump", + "name=logger,type=CephString,req=false " + "name=counter,type=CephString,req=false", + "dump perfcounters in osd and store"} + {} + seastar::future<tell_result_t> call(const cmdmap_t& cmdmap, + std::string_view format, + ceph::bufferlist&& input) const final + { + std::unique_ptr<Formatter> f{Formatter::create(format, + "json-pretty", + "json-pretty")}; + std::string logger; + std::string counter; + cmd_getval(cmdmap, "logger", logger); + cmd_getval(cmdmap, "counter", counter); + + crimson::common::local_perf_coll().dump_formatted(f.get(), false, false, logger, counter); + return seastar::make_ready_future<tell_result_t>(std::move(f)); + } +}; +template std::unique_ptr<AdminSocketHook> make_asok_hook<DumpPerfCountersHook>(); + + + +/** + * A CephContext admin hook: calling assert (if allowed by + * 'debug_asok_assert_abort') + */ +class AssertAlwaysHook : public AdminSocketHook { +public: + AssertAlwaysHook() : + AdminSocketHook{"assert", + "", + "asserts"} + {} + seastar::future<tell_result_t> call(const cmdmap_t&, + std::string_view format, + ceph::bufferlist&& input) const final + { + if (local_conf().get_val<bool>("debug_asok_assert_abort")) { + ceph_assert_always(0); + return seastar::make_ready_future<tell_result_t>(); + } else { + return seastar::make_ready_future<tell_result_t>( + tell_result_t{-EPERM, "configuration set to disallow asok assert"}); + } + } +}; +template std::unique_ptr<AdminSocketHook> make_asok_hook<AssertAlwaysHook>(); + +/** + * A Seastar admin hook: fetching the values of configured metrics + */ +class DumpMetricsHook : public AdminSocketHook { +public: + DumpMetricsHook() : + AdminSocketHook("dump_metrics", + "name=group,type=CephString,req=false", + "dump current configured seastar metrics and their values") + {} + seastar::future<tell_result_t> call(const cmdmap_t& cmdmap, + std::string_view format, + ceph::bufferlist&& input) const final + { + std::unique_ptr<Formatter> fref{Formatter::create(format, "json-pretty", "json-pretty")}; + auto *f = fref.get(); + std::string prefix; + cmd_getval(cmdmap, "group", prefix); + f->open_object_section("metrics"); + f->open_array_section("metrics"); + return seastar::do_with(std::move(prefix), [f](auto &prefix) { + return crimson::reactor_map_seq([f, &prefix] { + for (const auto& [full_name, metric_family]: seastar::scollectd::get_value_map()) { + if (!prefix.empty() && full_name.compare(0, prefix.size(), prefix) != 0) { + continue; + } + for (const auto& [labels, metric] : metric_family) { + if (metric && metric->is_enabled()) { + f->open_object_section(""); // enclosed by array + DumpMetricsHook::dump_metric_value(f, full_name, *metric, labels); + f->close_section(); + } + } + } + }); + }).then([fref = std::move(fref)]() mutable { + fref->close_section(); + fref->close_section(); + return seastar::make_ready_future<tell_result_t>(std::move(fref)); + }); + } +private: + using registered_metric = seastar::metrics::impl::registered_metric; + using data_type = seastar::metrics::impl::data_type; + + static void dump_metric_value(Formatter* f, + string_view full_name, + const registered_metric& metric, + const seastar::metrics::impl::labels_type& labels) + { + f->open_object_section(full_name); + for (const auto& [key, value] : labels) { + f->dump_string(key, value); + } + auto value_name = "value"; + switch (auto v = metric(); v.type()) { + case data_type::GAUGE: + f->dump_float(value_name, v.d()); + break; + case data_type::REAL_COUNTER: + f->dump_float(value_name, v.d()); + break; + case data_type::COUNTER: + double val; + try { + val = v.ui(); + } catch (std::range_error&) { + // seastar's cpu steal time may be negative + val = 0; + } + f->dump_unsigned(value_name, val); + break; + case data_type::HISTOGRAM: { + f->open_object_section(value_name); + auto&& h = v.get_histogram(); + f->dump_float("sum", h.sample_sum); + f->dump_unsigned("count", h.sample_count); + f->open_array_section("buckets"); + for (auto i : h.buckets) { + f->open_object_section("bucket"); + f->dump_float("le", i.upper_bound); + f->dump_unsigned("count", i.count); + f->close_section(); // "bucket" + } + { + f->open_object_section("bucket"); + f->dump_string("le", "+Inf"); + f->dump_unsigned("count", h.sample_count); + f->close_section(); + } + f->close_section(); // "buckets" + f->close_section(); // value_name + } + break; + default: + std::abort(); + break; + } + f->close_section(); // full_name + } +}; +template std::unique_ptr<AdminSocketHook> make_asok_hook<DumpMetricsHook>(); + + +static ghobject_t test_ops_get_object_name( + const OSDMap& osdmap, + const cmdmap_t& cmdmap) +{ + auto pool = [&] { + auto pool_arg = cmd_getval<std::string>(cmdmap, "pool"); + if (!pool_arg) { + throw std::invalid_argument{"No 'pool' specified"}; + } + int64_t pool = osdmap.lookup_pg_pool_name(*pool_arg); + if (pool < 0 && std::isdigit((*pool_arg)[0])) { + pool = std::atoll(pool_arg->c_str()); + } + if (pool < 0) { + // the return type of `fmt::format` is `std::string` + throw std::invalid_argument{ + fmt::format("Invalid pool '{}'", *pool_arg) + }; + } + return pool; + }(); + + auto [ objname, nspace, raw_pg ] = [&] { + auto obj_arg = cmd_getval<std::string>(cmdmap, "objname"); + if (!obj_arg) { + throw std::invalid_argument{"No 'objname' specified"}; + } + std::string objname, nspace; + if (std::size_t sep_pos = obj_arg->find_first_of('/'); + sep_pos != obj_arg->npos) { + nspace = obj_arg->substr(0, sep_pos); + objname = obj_arg->substr(sep_pos+1); + } else { + objname = *obj_arg; + } + pg_t raw_pg; + if (object_locator_t oloc(pool, nspace); + osdmap.object_locator_to_pg(object_t(objname), oloc, raw_pg) < 0) { + throw std::invalid_argument{"Invalid namespace/objname"}; + } + return std::make_tuple(std::move(objname), + std::move(nspace), + std::move(raw_pg)); + }(); + + auto shard_id = cmd_getval_or<int64_t>(cmdmap, + "shardid", + shard_id_t::NO_SHARD); + + return ghobject_t{ + hobject_t{ + object_t{objname}, std::string{}, CEPH_NOSNAP, raw_pg.ps(), pool, nspace + }, + ghobject_t::NO_GEN, + shard_id_t{static_cast<int8_t>(shard_id)} + }; +} + +// Usage: +// injectdataerr <pool> [namespace/]<obj-name> [shardid] +class InjectDataErrorHook : public AdminSocketHook { +public: + InjectDataErrorHook(crimson::osd::ShardServices& shard_services) : + AdminSocketHook("injectdataerr", + "name=pool,type=CephString " \ + "name=objname,type=CephObjectname " \ + "name=shardid,type=CephInt,req=false,range=0|255", + "inject data error to an object"), + shard_services(shard_services) { + } + + seastar::future<tell_result_t> call(const cmdmap_t& cmdmap, + std::string_view format, + ceph::bufferlist&& input) const final + { + ghobject_t obj; + try { + obj = test_ops_get_object_name(*shard_services.get_map(), cmdmap); + } catch (const std::invalid_argument& e) { + logger().info("error during data error injection: {}", e.what()); + return seastar::make_ready_future<tell_result_t>(-EINVAL, + e.what()); + } + return shard_services.get_store().inject_data_error(obj).then([=] { + logger().info("successfully injected data error for obj={}", obj); + ceph::bufferlist bl; + bl.append("ok"sv); + return seastar::make_ready_future<tell_result_t>(0, + std::string{}, // no err + std::move(bl)); + }); + } + +private: + crimson::osd::ShardServices& shard_services; +}; +template std::unique_ptr<AdminSocketHook> make_asok_hook<InjectDataErrorHook>( + crimson::osd::ShardServices&); + + +// Usage: +// injectmdataerr <pool> [namespace/]<obj-name> [shardid] +class InjectMDataErrorHook : public AdminSocketHook { +public: + InjectMDataErrorHook(crimson::osd::ShardServices& shard_services) : + AdminSocketHook("injectmdataerr", + "name=pool,type=CephString " \ + "name=objname,type=CephObjectname " \ + "name=shardid,type=CephInt,req=false,range=0|255", + "inject data error to an object"), + shard_services(shard_services) { + } + + seastar::future<tell_result_t> call(const cmdmap_t& cmdmap, + std::string_view format, + ceph::bufferlist&& input) const final + { + ghobject_t obj; + try { + obj = test_ops_get_object_name(*shard_services.get_map(), cmdmap); + } catch (const std::invalid_argument& e) { + logger().info("error during metadata error injection: {}", e.what()); + return seastar::make_ready_future<tell_result_t>(-EINVAL, + e.what()); + } + return shard_services.get_store().inject_mdata_error(obj).then([=] { + logger().info("successfully injected metadata error for obj={}", obj); + ceph::bufferlist bl; + bl.append("ok"sv); + return seastar::make_ready_future<tell_result_t>(0, + std::string{}, // no err + std::move(bl)); + }); + } + +private: + crimson::osd::ShardServices& shard_services; +}; +template std::unique_ptr<AdminSocketHook> make_asok_hook<InjectMDataErrorHook>( + crimson::osd::ShardServices&); + + +/** + * An InFlightOps admin hook: dump current in-flight operations + */ +class DumpInFlightOpsHook : public AdminSocketHook { +public: + explicit DumpInFlightOpsHook(const crimson::osd::PGShardManager &pg_shard_manager) : + AdminSocketHook{"dump_ops_in_flight", "", "show the ops currently in flight"}, + pg_shard_manager(pg_shard_manager) + {} + seastar::future<tell_result_t> call(const cmdmap_t&, + std::string_view format, + ceph::bufferlist&& input) const final + { + unique_ptr<Formatter> fref{ + Formatter::create(format, "json-pretty", "json-pretty")}; + auto *f = fref.get(); + f->open_object_section("ops_in_flight"); + f->open_array_section("ops_in_flight"); + return pg_shard_manager.invoke_on_each_shard_seq([f](const auto &shard_services) { + return shard_services.dump_ops_in_flight(f); + }).then([fref=std::move(fref)]() mutable { + fref->close_section(); + fref->close_section(); + return seastar::make_ready_future<tell_result_t>(std::move(fref)); + }); + } +private: + const crimson::osd::PGShardManager &pg_shard_manager; +}; +template std::unique_ptr<AdminSocketHook> +make_asok_hook<DumpInFlightOpsHook>(const crimson::osd::PGShardManager &); + + +class DumpHistoricOpsHook : public AdminSocketHook { +public: + explicit DumpHistoricOpsHook(const crimson::osd::OSDOperationRegistry& op_registry) : + AdminSocketHook{"dump_historic_ops", "", "show recent ops"}, + op_registry(op_registry) + {} + seastar::future<tell_result_t> call(const cmdmap_t&, + std::string_view format, + ceph::bufferlist&& input) const final + { + unique_ptr<Formatter> f{Formatter::create(format, "json-pretty", "json-pretty")}; + f->open_object_section("historic_ops"); + op_registry.dump_historic_client_requests(f.get()); + f->close_section(); + f->dump_int("num_ops", 0); + return seastar::make_ready_future<tell_result_t>(std::move(f)); + } +private: + const crimson::osd::OSDOperationRegistry& op_registry; +}; +template std::unique_ptr<AdminSocketHook> +make_asok_hook<DumpHistoricOpsHook>(const crimson::osd::OSDOperationRegistry& op_registry); + + +class DumpSlowestHistoricOpsHook : public AdminSocketHook { +public: + explicit DumpSlowestHistoricOpsHook(const crimson::osd::OSDOperationRegistry& op_registry) : + AdminSocketHook{"dump_historic_slow_ops", "", "show slowest recent ops"}, + op_registry(op_registry) + {} + seastar::future<tell_result_t> call(const cmdmap_t&, + std::string_view format, + ceph::bufferlist&& input) const final + { + logger().warn("{}", __func__); + unique_ptr<Formatter> f{Formatter::create(format, "json-pretty", "json-pretty")}; + f->open_object_section("historic_slow_ops"); + op_registry.dump_slowest_historic_client_requests(f.get()); + f->close_section(); + f->dump_int("num_ops", 0); + return seastar::make_ready_future<tell_result_t>(std::move(f)); + } +private: + const crimson::osd::OSDOperationRegistry& op_registry; +}; +template std::unique_ptr<AdminSocketHook> +make_asok_hook<DumpSlowestHistoricOpsHook>(const crimson::osd::OSDOperationRegistry& op_registry); + +class DumpRecoveryReservationsHook : public AdminSocketHook { +public: + explicit DumpRecoveryReservationsHook(crimson::osd::ShardServices& shard_services) : + AdminSocketHook{"dump_recovery_reservations", "", "show recovery reservations"}, + shard_services(shard_services) + {} + seastar::future<tell_result_t> call(const cmdmap_t&, + std::string_view format, + ceph::bufferlist&& input) const final + { + logger().debug("{}", __func__); + unique_ptr<Formatter> f{Formatter::create(format, "json-pretty", "json-pretty")}; + return seastar::do_with(std::move(f), [this](auto&& f) { + f->open_object_section("reservations"); + f->open_object_section("local_reservations"); + return shard_services.local_dump_reservations(f.get()).then([&f, this] { + f->close_section(); + f->open_object_section("remote_reservations"); + return shard_services.remote_dump_reservations(f.get()).then([&f] { + f->close_section(); + f->close_section(); + return seastar::make_ready_future<tell_result_t>(std::move(f)); + }); + }); + }); + } +private: + crimson::osd::ShardServices& shard_services; +}; +template std::unique_ptr<AdminSocketHook> +make_asok_hook<DumpRecoveryReservationsHook>(crimson::osd::ShardServices& shard_services); + +} // namespace crimson::admin |