]> git.proxmox.com Git - ceph.git/blob - ceph/src/crimson/admin/osd_admin.cc
e8320458834773f0eb04333a8903bb6632522cfb
[ceph.git] / ceph / src / crimson / admin / osd_admin.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3
4 #include "crimson/admin/osd_admin.h"
5 #include <string>
6 #include <string_view>
7
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>
13
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"
19
20 namespace {
21 seastar::logger& logger()
22 {
23 return crimson::get_logger(ceph_subsys_osd);
24 }
25 } // namespace
26
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;
32
33 namespace crimson::admin {
34
35 template <class Hook, class... Args>
36 std::unique_ptr<AdminSocketHook> make_asok_hook(Args&&... args)
37 {
38 return std::make_unique<Hook>(std::forward<Args>(args)...);
39 }
40
41 /**
42 * An OSD admin hook: OSD status
43 */
44 class OsdStatusHook : public AdminSocketHook {
45 public:
46 explicit OsdStatusHook(const crimson::osd::OSD& osd) :
47 AdminSocketHook{"status", "", "OSD status"},
48 osd(osd)
49 {}
50 seastar::future<tell_result_t> call(const cmdmap_t&,
51 std::string_view format,
52 ceph::bufferlist&& input) const final
53 {
54 unique_ptr<Formatter> f{Formatter::create(format, "json-pretty", "json-pretty")};
55 f->open_object_section("status");
56 osd.dump_status(f.get());
57 f->close_section();
58 return seastar::make_ready_future<tell_result_t>(std::move(f));
59 }
60 private:
61 const crimson::osd::OSD& osd;
62 };
63 template std::unique_ptr<AdminSocketHook>
64 make_asok_hook<OsdStatusHook>(const crimson::osd::OSD& osd);
65
66 /**
67 * An OSD admin hook: send beacon
68 */
69 class SendBeaconHook : public AdminSocketHook {
70 public:
71 explicit SendBeaconHook(crimson::osd::OSD& osd) :
72 AdminSocketHook{"send_beacon",
73 "",
74 "send OSD beacon to mon immediately"},
75 osd(osd)
76 {}
77 seastar::future<tell_result_t> call(const cmdmap_t&,
78 std::string_view format,
79 ceph::bufferlist&& input) const final
80 {
81 return osd.send_beacon().then([] {
82 return seastar::make_ready_future<tell_result_t>();
83 });
84 }
85 private:
86 crimson::osd::OSD& osd;
87 };
88 template std::unique_ptr<AdminSocketHook>
89 make_asok_hook<SendBeaconHook>(crimson::osd::OSD& osd);
90
91 /**
92 * send the latest pg stats to mgr
93 */
94 class FlushPgStatsHook : public AdminSocketHook {
95 public:
96 explicit FlushPgStatsHook(crimson::osd::OSD& osd) :
97 AdminSocketHook("flush_pg_stats",
98 "",
99 "flush pg stats"),
100 osd{osd}
101 {}
102 seastar::future<tell_result_t> call(const cmdmap_t&,
103 std::string_view format,
104 ceph::bufferlist&& input) const final
105 {
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));
110 }
111
112 private:
113 crimson::osd::OSD& osd;
114 };
115 template std::unique_ptr<AdminSocketHook> make_asok_hook<FlushPgStatsHook>(crimson::osd::OSD& osd);
116
117 /// dump the history of PGs' peering state
118 class DumpPGStateHistory final: public AdminSocketHook {
119 public:
120 explicit DumpPGStateHistory(const crimson::osd::OSD &osd) :
121 AdminSocketHook{"dump_pgstate_history",
122 "",
123 "dump history of PGs' peering state"},
124 osd{osd}
125 {}
126 seastar::future<tell_result_t> call(const cmdmap_t&,
127 std::string_view format,
128 ceph::bufferlist&& input) const final
129 {
130 std::unique_ptr<Formatter> f{Formatter::create(format,
131 "json-pretty",
132 "json-pretty")};
133 f->open_object_section("pgstate_history");
134 osd.dump_pg_state_history(f.get());
135 f->close_section();
136 return seastar::make_ready_future<tell_result_t>(std::move(f));
137 }
138 private:
139 const crimson::osd::OSD& osd;
140 };
141 template std::unique_ptr<AdminSocketHook> make_asok_hook<DumpPGStateHistory>(const crimson::osd::OSD& osd);
142
143 //dump the contents of perfcounters in osd and store
144 class DumpPerfCountersHook final: public AdminSocketHook {
145 public:
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"}
151 {}
152 seastar::future<tell_result_t> call(const cmdmap_t& cmdmap,
153 std::string_view format,
154 ceph::bufferlist&& input) const final
155 {
156 std::unique_ptr<Formatter> f{Formatter::create(format,
157 "json-pretty",
158 "json-pretty")};
159 std::string logger;
160 std::string counter;
161 cmd_getval(cmdmap, "logger", logger);
162 cmd_getval(cmdmap, "counter", counter);
163
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));
166 }
167 };
168 template std::unique_ptr<AdminSocketHook> make_asok_hook<DumpPerfCountersHook>();
169
170
171
172 /**
173 * A CephContext admin hook: calling assert (if allowed by
174 * 'debug_asok_assert_abort')
175 */
176 class AssertAlwaysHook : public AdminSocketHook {
177 public:
178 AssertAlwaysHook() :
179 AdminSocketHook{"assert",
180 "",
181 "asserts"}
182 {}
183 seastar::future<tell_result_t> call(const cmdmap_t&,
184 std::string_view format,
185 ceph::bufferlist&& input) const final
186 {
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>();
190 } else {
191 return seastar::make_ready_future<tell_result_t>(
192 tell_result_t{-EPERM, "configuration set to disallow asok assert"});
193 }
194 }
195 };
196 template std::unique_ptr<AdminSocketHook> make_asok_hook<AssertAlwaysHook>();
197
198 /**
199 * A Seastar admin hook: fetching the values of configured metrics
200 */
201 class DumpMetricsHook : public AdminSocketHook {
202 public:
203 DumpMetricsHook() :
204 AdminSocketHook("dump_metrics",
205 "name=group,type=CephString,req=false",
206 "dump current configured seastar metrics and their values")
207 {}
208 seastar::future<tell_result_t> call(const cmdmap_t& cmdmap,
209 std::string_view format,
210 ceph::bufferlist&& input) const final
211 {
212 std::unique_ptr<Formatter> f{Formatter::create(format, "json-pretty", "json-pretty")};
213 std::string prefix;
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) {
218 continue;
219 }
220 for (const auto& [labels, metric] : metric_family) {
221 if (metric && metric->is_enabled()) {
222 dump_metric_value(f.get(), full_name, *metric, labels);
223 }
224 }
225 }
226 f->close_section();
227 return seastar::make_ready_future<tell_result_t>(std::move(f));
228 }
229 private:
230 using registered_metric = seastar::metrics::impl::registered_metric;
231 using data_type = seastar::metrics::impl::data_type;
232
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)
237 {
238 f->open_object_section(full_name);
239 for (const auto& [key, value] : labels) {
240 f->dump_string(key, value);
241 }
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());
246 break;
247 case data_type::COUNTER:
248 f->dump_unsigned(value_name, v.ui());
249 break;
250 case data_type::DERIVE:
251 f->dump_int(value_name, v.i());
252 break;
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"
264 }
265 {
266 f->open_object_section("bucket");
267 f->dump_string("le", "+Inf");
268 f->dump_unsigned("count", h.sample_count);
269 f->close_section();
270 }
271 f->close_section(); // "buckets"
272 f->close_section(); // value_name
273 }
274 break;
275 default:
276 std::abort();
277 break;
278 }
279 f->close_section(); // full_name
280 }
281 };
282 template std::unique_ptr<AdminSocketHook> make_asok_hook<DumpMetricsHook>();
283
284
285 static ghobject_t test_ops_get_object_name(
286 const OSDMap& osdmap,
287 const cmdmap_t& cmdmap)
288 {
289 auto pool = [&] {
290 auto pool_arg = cmd_getval<std::string>(cmdmap, "pool");
291 if (!pool_arg) {
292 throw std::invalid_argument{"No 'pool' specified"};
293 }
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());
297 }
298 if (pool < 0) {
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)
303 };
304 }
305 return pool;
306 }();
307
308 auto [ objname, nspace, raw_pg ] = [&] {
309 auto obj_arg = cmd_getval<std::string>(cmdmap, "objname");
310 if (!obj_arg) {
311 throw std::invalid_argument{"No 'objname' specified"};
312 }
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);
318 } else {
319 objname = *obj_arg;
320 }
321 pg_t raw_pg;
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"};
325 }
326 return std::make_tuple(std::move(objname),
327 std::move(nspace),
328 std::move(raw_pg));
329 }();
330
331 auto shard_id = cmd_getval_or<int64_t>(cmdmap,
332 "shardid",
333 shard_id_t::NO_SHARD);
334
335 return ghobject_t{
336 hobject_t{
337 object_t{objname}, std::string{}, CEPH_NOSNAP, raw_pg.ps(), pool, nspace
338 },
339 ghobject_t::NO_GEN,
340 shard_id_t{static_cast<int8_t>(shard_id)}
341 };
342 }
343
344 // Usage:
345 // injectdataerr <pool> [namespace/]<obj-name> [shardid]
346 class InjectDataErrorHook : public AdminSocketHook {
347 public:
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) {
355 }
356
357 seastar::future<tell_result_t> call(const cmdmap_t& cmdmap,
358 std::string_view format,
359 ceph::bufferlist&& input) const final
360 {
361 ghobject_t obj;
362 try {
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,
367 e.what());
368 }
369 return shard_services.get_store().inject_data_error(obj).then([=] {
370 logger().info("successfully injected data error for obj={}", obj);
371 ceph::bufferlist bl;
372 bl.append("ok"sv);
373 return seastar::make_ready_future<tell_result_t>(0,
374 std::string{}, // no err
375 std::move(bl));
376 });
377 }
378
379 private:
380 crimson::osd::ShardServices& shard_services;
381 };
382 template std::unique_ptr<AdminSocketHook> make_asok_hook<InjectDataErrorHook>(
383 crimson::osd::ShardServices&);
384
385
386 // Usage:
387 // injectmdataerr <pool> [namespace/]<obj-name> [shardid]
388 class InjectMDataErrorHook : public AdminSocketHook {
389 public:
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) {
397 }
398
399 seastar::future<tell_result_t> call(const cmdmap_t& cmdmap,
400 std::string_view format,
401 ceph::bufferlist&& input) const final
402 {
403 ghobject_t obj;
404 try {
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,
409 e.what());
410 }
411 return shard_services.get_store().inject_mdata_error(obj).then([=] {
412 logger().info("successfully injected metadata error for obj={}", obj);
413 ceph::bufferlist bl;
414 bl.append("ok"sv);
415 return seastar::make_ready_future<tell_result_t>(0,
416 std::string{}, // no err
417 std::move(bl));
418 });
419 }
420
421 private:
422 crimson::osd::ShardServices& shard_services;
423 };
424 template std::unique_ptr<AdminSocketHook> make_asok_hook<InjectMDataErrorHook>(
425 crimson::osd::ShardServices&);
426
427 } // namespace crimson::admin