1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 #include "crimson/admin/osd_admin.h"
8 #include <fmt/format.h>
9 #include <seastar/core/do_with.hh>
10 #include <seastar/core/future.hh>
11 #include <seastar/core/thread.hh>
12 #include <seastar/core/scollectd_api.hh>
14 #include "common/config.h"
15 #include "crimson/admin/admin_socket.h"
16 #include "crimson/common/log.h"
17 #include "crimson/osd/exceptions.h"
18 #include "crimson/osd/osd.h"
21 seastar::logger
& logger()
23 return crimson::get_logger(ceph_subsys_osd
);
27 using std::string_view
;
28 using std::unique_ptr
;
29 using crimson::osd::OSD
;
30 using crimson::common::local_conf
;
31 using namespace crimson::common
;
33 namespace crimson::admin
{
35 template <class Hook
, class... Args
>
36 std::unique_ptr
<AdminSocketHook
> make_asok_hook(Args
&&... args
)
38 return std::make_unique
<Hook
>(std::forward
<Args
>(args
)...);
42 * An OSD admin hook: OSD status
44 class OsdStatusHook
: public AdminSocketHook
{
46 explicit OsdStatusHook(const crimson::osd::OSD
& osd
) :
47 AdminSocketHook
{"status", "", "OSD status"},
50 seastar::future
<tell_result_t
> call(const cmdmap_t
&,
51 std::string_view format
,
52 ceph::bufferlist
&& input
) const final
54 unique_ptr
<Formatter
> f
{Formatter::create(format
, "json-pretty", "json-pretty")};
55 f
->open_object_section("status");
56 osd
.dump_status(f
.get());
58 return seastar::make_ready_future
<tell_result_t
>(std::move(f
));
61 const crimson::osd::OSD
& osd
;
63 template std::unique_ptr
<AdminSocketHook
>
64 make_asok_hook
<OsdStatusHook
>(const crimson::osd::OSD
& osd
);
67 * An OSD admin hook: send beacon
69 class SendBeaconHook
: public AdminSocketHook
{
71 explicit SendBeaconHook(crimson::osd::OSD
& osd
) :
72 AdminSocketHook
{"send_beacon",
74 "send OSD beacon to mon immediately"},
77 seastar::future
<tell_result_t
> call(const cmdmap_t
&,
78 std::string_view format
,
79 ceph::bufferlist
&& input
) const final
81 return osd
.send_beacon().then([] {
82 return seastar::make_ready_future
<tell_result_t
>();
86 crimson::osd::OSD
& osd
;
88 template std::unique_ptr
<AdminSocketHook
>
89 make_asok_hook
<SendBeaconHook
>(crimson::osd::OSD
& osd
);
92 * send the latest pg stats to mgr
94 class FlushPgStatsHook
: public AdminSocketHook
{
96 explicit FlushPgStatsHook(crimson::osd::OSD
& osd
) :
97 AdminSocketHook("flush_pg_stats",
102 seastar::future
<tell_result_t
> call(const cmdmap_t
&,
103 std::string_view format
,
104 ceph::bufferlist
&& input
) const final
106 uint64_t seq
= osd
.send_pg_stats();
107 unique_ptr
<Formatter
> f
{Formatter::create(format
, "json-pretty", "json-pretty")};
108 f
->dump_unsigned("stat_seq", seq
);
109 return seastar::make_ready_future
<tell_result_t
>(std::move(f
));
113 crimson::osd::OSD
& osd
;
115 template std::unique_ptr
<AdminSocketHook
> make_asok_hook
<FlushPgStatsHook
>(crimson::osd::OSD
& osd
);
117 /// dump the history of PGs' peering state
118 class DumpPGStateHistory final
: public AdminSocketHook
{
120 explicit DumpPGStateHistory(const crimson::osd::OSD
&osd
) :
121 AdminSocketHook
{"dump_pgstate_history",
123 "dump history of PGs' peering state"},
126 seastar::future
<tell_result_t
> call(const cmdmap_t
&,
127 std::string_view format
,
128 ceph::bufferlist
&& input
) const final
130 std::unique_ptr
<Formatter
> f
{Formatter::create(format
,
133 f
->open_object_section("pgstate_history");
134 osd
.dump_pg_state_history(f
.get());
136 return seastar::make_ready_future
<tell_result_t
>(std::move(f
));
139 const crimson::osd::OSD
& osd
;
141 template std::unique_ptr
<AdminSocketHook
> make_asok_hook
<DumpPGStateHistory
>(const crimson::osd::OSD
& osd
);
143 //dump the contents of perfcounters in osd and store
144 class DumpPerfCountersHook final
: public AdminSocketHook
{
146 explicit DumpPerfCountersHook() :
147 AdminSocketHook
{"perfcounters_dump",
148 "name=logger,type=CephString,req=false "
149 "name=counter,type=CephString,req=false",
150 "dump perfcounters in osd and store"}
152 seastar::future
<tell_result_t
> call(const cmdmap_t
& cmdmap
,
153 std::string_view format
,
154 ceph::bufferlist
&& input
) const final
156 std::unique_ptr
<Formatter
> f
{Formatter::create(format
,
161 cmd_getval(cmdmap
, "logger", logger
);
162 cmd_getval(cmdmap
, "counter", counter
);
164 crimson::common::local_perf_coll().dump_formatted(f
.get(), false, logger
, counter
);
165 return seastar::make_ready_future
<tell_result_t
>(std::move(f
));
168 template std::unique_ptr
<AdminSocketHook
> make_asok_hook
<DumpPerfCountersHook
>();
173 * A CephContext admin hook: calling assert (if allowed by
174 * 'debug_asok_assert_abort')
176 class AssertAlwaysHook
: public AdminSocketHook
{
179 AdminSocketHook
{"assert",
183 seastar::future
<tell_result_t
> call(const cmdmap_t
&,
184 std::string_view format
,
185 ceph::bufferlist
&& input
) const final
187 if (local_conf().get_val
<bool>("debug_asok_assert_abort")) {
188 ceph_assert_always(0);
189 return seastar::make_ready_future
<tell_result_t
>();
191 return seastar::make_ready_future
<tell_result_t
>(
192 tell_result_t
{-EPERM
, "configuration set to disallow asok assert"});
196 template std::unique_ptr
<AdminSocketHook
> make_asok_hook
<AssertAlwaysHook
>();
199 * A Seastar admin hook: fetching the values of configured metrics
201 class DumpMetricsHook
: public AdminSocketHook
{
204 AdminSocketHook("dump_metrics",
205 "name=group,type=CephString,req=false",
206 "dump current configured seastar metrics and their values")
208 seastar::future
<tell_result_t
> call(const cmdmap_t
& cmdmap
,
209 std::string_view format
,
210 ceph::bufferlist
&& input
) const final
212 std::unique_ptr
<Formatter
> f
{Formatter::create(format
, "json-pretty", "json-pretty")};
214 cmd_getval(cmdmap
, "group", prefix
);
215 f
->open_object_section("metrics");
216 for (const auto& [full_name
, metric_family
]: seastar::scollectd::get_value_map()) {
217 if (!prefix
.empty() && full_name
.compare(0, prefix
.size(), prefix
) != 0) {
220 for (const auto& [labels
, metric
] : metric_family
) {
221 if (metric
&& metric
->is_enabled()) {
222 dump_metric_value(f
.get(), full_name
, *metric
, labels
);
227 return seastar::make_ready_future
<tell_result_t
>(std::move(f
));
230 using registered_metric
= seastar::metrics::impl::registered_metric
;
231 using data_type
= seastar::metrics::impl::data_type
;
233 static void dump_metric_value(Formatter
* f
,
234 string_view full_name
,
235 const registered_metric
& metric
,
236 const seastar::metrics::impl::labels_type
& labels
)
238 f
->open_object_section(full_name
);
239 for (const auto& [key
, value
] : labels
) {
240 f
->dump_string(key
, value
);
242 auto value_name
= "value";
243 switch (auto v
= metric(); v
.type()) {
244 case data_type::GAUGE
:
245 f
->dump_float(value_name
, v
.d());
247 case data_type::COUNTER
:
248 f
->dump_unsigned(value_name
, v
.ui());
250 case data_type::DERIVE
:
251 f
->dump_int(value_name
, v
.i());
253 case data_type::HISTOGRAM
: {
254 f
->open_object_section(value_name
);
255 auto&& h
= v
.get_histogram();
256 f
->dump_float("sum", h
.sample_sum
);
257 f
->dump_unsigned("count", h
.sample_count
);
258 f
->open_array_section("buckets");
259 for (auto i
: h
.buckets
) {
260 f
->open_object_section("bucket");
261 f
->dump_float("le", i
.upper_bound
);
262 f
->dump_unsigned("count", i
.count
);
263 f
->close_section(); // "bucket"
266 f
->open_object_section("bucket");
267 f
->dump_string("le", "+Inf");
268 f
->dump_unsigned("count", h
.sample_count
);
271 f
->close_section(); // "buckets"
272 f
->close_section(); // value_name
279 f
->close_section(); // full_name
282 template std::unique_ptr
<AdminSocketHook
> make_asok_hook
<DumpMetricsHook
>();
285 static ghobject_t
test_ops_get_object_name(
286 const OSDMap
& osdmap
,
287 const cmdmap_t
& cmdmap
)
290 auto pool_arg
= cmd_getval
<std::string
>(cmdmap
, "pool");
292 throw std::invalid_argument
{"No 'pool' specified"};
294 int64_t pool
= osdmap
.lookup_pg_pool_name(*pool_arg
);
295 if (pool
< 0 && std::isdigit((*pool_arg
)[0])) {
296 pool
= std::atoll(pool_arg
->c_str());
299 // the return type of `fmt::format` is `std::string`
300 using namespace fmt::literals
;
301 throw std::invalid_argument
{
302 "Invalid pool '{}'"_format(*pool_arg
)
308 auto [ objname
, nspace
, raw_pg
] = [&] {
309 auto obj_arg
= cmd_getval
<std::string
>(cmdmap
, "objname");
311 throw std::invalid_argument
{"No 'objname' specified"};
313 std::string objname
, nspace
;
314 if (std::size_t sep_pos
= obj_arg
->find_first_of('/');
315 sep_pos
!= obj_arg
->npos
) {
316 nspace
= obj_arg
->substr(0, sep_pos
);
317 objname
= obj_arg
->substr(sep_pos
+1);
322 if (object_locator_t
oloc(pool
, nspace
);
323 osdmap
.object_locator_to_pg(object_t(objname
), oloc
, raw_pg
) < 0) {
324 throw std::invalid_argument
{"Invalid namespace/objname"};
326 return std::make_tuple(std::move(objname
),
331 auto shard_id
= cmd_getval_or
<int64_t>(cmdmap
,
333 shard_id_t::NO_SHARD
);
337 object_t
{objname
}, std::string
{}, CEPH_NOSNAP
, raw_pg
.ps(), pool
, nspace
340 shard_id_t
{static_cast<int8_t>(shard_id
)}
345 // injectdataerr <pool> [namespace/]<obj-name> [shardid]
346 class InjectDataErrorHook
: public AdminSocketHook
{
348 InjectDataErrorHook(crimson::osd::ShardServices
& shard_services
) :
349 AdminSocketHook("injectdataerr",
350 "name=pool,type=CephString " \
351 "name=objname,type=CephObjectname " \
352 "name=shardid,type=CephInt,req=false,range=0|255",
353 "inject data error to an object"),
354 shard_services(shard_services
) {
357 seastar::future
<tell_result_t
> call(const cmdmap_t
& cmdmap
,
358 std::string_view format
,
359 ceph::bufferlist
&& input
) const final
363 obj
= test_ops_get_object_name(*shard_services
.get_osdmap(), cmdmap
);
364 } catch (const std::invalid_argument
& e
) {
365 logger().info("error during data error injection: {}", e
.what());
366 return seastar::make_ready_future
<tell_result_t
>(-EINVAL
,
369 return shard_services
.get_store().inject_data_error(obj
).then([=] {
370 logger().info("successfully injected data error for obj={}", obj
);
373 return seastar::make_ready_future
<tell_result_t
>(0,
374 std::string
{}, // no err
380 crimson::osd::ShardServices
& shard_services
;
382 template std::unique_ptr
<AdminSocketHook
> make_asok_hook
<InjectDataErrorHook
>(
383 crimson::osd::ShardServices
&);
387 // injectmdataerr <pool> [namespace/]<obj-name> [shardid]
388 class InjectMDataErrorHook
: public AdminSocketHook
{
390 InjectMDataErrorHook(crimson::osd::ShardServices
& shard_services
) :
391 AdminSocketHook("injectmdataerr",
392 "name=pool,type=CephString " \
393 "name=objname,type=CephObjectname " \
394 "name=shardid,type=CephInt,req=false,range=0|255",
395 "inject data error to an object"),
396 shard_services(shard_services
) {
399 seastar::future
<tell_result_t
> call(const cmdmap_t
& cmdmap
,
400 std::string_view format
,
401 ceph::bufferlist
&& input
) const final
405 obj
= test_ops_get_object_name(*shard_services
.get_osdmap(), cmdmap
);
406 } catch (const std::invalid_argument
& e
) {
407 logger().info("error during metadata error injection: {}", e
.what());
408 return seastar::make_ready_future
<tell_result_t
>(-EINVAL
,
411 return shard_services
.get_store().inject_mdata_error(obj
).then([=] {
412 logger().info("successfully injected metadata error for obj={}", obj
);
415 return seastar::make_ready_future
<tell_result_t
>(0,
416 std::string
{}, // no err
422 crimson::osd::ShardServices
& shard_services
;
424 template std::unique_ptr
<AdminSocketHook
> make_asok_hook
<InjectMDataErrorHook
>(
425 crimson::osd::ShardServices
&);
427 } // namespace crimson::admin