]>
git.proxmox.com Git - ceph.git/blob - ceph/src/crimson/admin/admin_socket.cc
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/admin_socket.h"
6 #include <fmt/format.h>
7 #include <seastar/net/api.hh>
8 #include <seastar/net/inet_address.hh>
9 #include <seastar/core/reactor.hh>
10 #include <seastar/core/sleep.hh>
11 #include <seastar/core/thread.hh>
12 #include <seastar/util/std-compat.hh>
14 #include "common/version.h"
15 #include "crimson/common/log.h"
16 #include "crimson/net/Socket.h"
18 using namespace crimson::common
;
20 * A Crimson-wise version of the admin socket - implementation file
22 * \todo handle the unlinking of the admin socket. Note that 'cleanup_files'
23 * at-exit functionality is not yet implemented in Crimson.
27 seastar::logger
& logger()
29 return crimson::get_logger(ceph_subsys_osd
);
33 namespace crimson::admin
{
36 AdminSocket::register_command(std::unique_ptr
<AdminSocketHook
>&& hook
)
38 return seastar::with_lock(servers_tbl_rwlock
,
39 [this, hook
= std::move(hook
)]() mutable {
40 auto prefix
= hook
->prefix
;
41 auto [it
, added
] = hooks
.emplace(prefix
, std::move(hook
));
42 // was this server tag already registered?
45 logger().info("register_command(): {})", it
->first
);
47 return seastar::now();
52 * Note: parse_cmd() is executed with servers_tbl_rwlock held as shared
54 AdminSocket::maybe_parsed_t
AdminSocket::parse_cmd(std::string cmd
,
55 ceph::bufferlist
& out
)
58 // - create the formatter specified by the cmd parameters
59 // - locate the "op-code" string (the 'prefix' segment)
60 // - prepare for command parameters extraction via cmdmap_t
65 // note that cmdmap_from_json() may throw on syntax issues
66 if (!cmdmap_from_json({cmd
}, &cmdmap
, errss
)) {
67 logger().error("{}: incoming command error: {}", __func__
, errss
.str());
68 out
.append("error:"s
);
69 out
.append(errss
.str());
70 return maybe_parsed_t
{ std::nullopt
};
72 } catch (std::runtime_error
& e
) {
73 logger().error("{}: incoming command syntax: {}", __func__
, cmd
);
74 out
.append("error: command syntax"s
);
75 return maybe_parsed_t
{ std::nullopt
};
81 cmd_getval(cmdmap
, "format", format
);
82 cmd_getval(cmdmap
, "prefix", prefix
);
83 } catch (const bad_cmd_get
& e
) {
84 logger().error("{}: invalid syntax: {}", __func__
, cmd
);
85 out
.append("error: command syntax: missing 'prefix'"s
);
86 return maybe_parsed_t
{ std::nullopt
};
90 // no command identified
91 out
.append("error: no command identified"s
);
92 return maybe_parsed_t
{ std::nullopt
};
95 // match the incoming op-code to one of the registered APIs
96 if (auto found
= hooks
.find(prefix
); found
!= hooks
.end()) {
97 return parsed_command_t
{ cmdmap
, format
, *found
->second
};
99 return maybe_parsed_t
{ std::nullopt
};
104 * Note: validate_command() is executed with servers_tbl_rwlock held as shared
106 bool AdminSocket::validate_command(const parsed_command_t
& parsed
,
107 const std::string
& command_text
,
108 ceph::bufferlist
& out
) const
110 logger().info("{}: validating {} against:{}", __func__
, command_text
,
113 stringstream os
; // for possible validation error messages
114 if (validate_cmd(nullptr, std::string
{parsed
.hook
.desc
}, parsed
.parameters
, os
)) {
117 os
<< "error: command validation failure ";
118 logger().error("{}: validation failure (incoming:{}) {}", __func__
,
119 command_text
, os
.str());
125 seastar::future
<> AdminSocket::finalize_response(
126 seastar::output_stream
<char>& out
, ceph::bufferlist
&& msgs
)
128 string outbuf_cont
= msgs
.to_str();
129 if (outbuf_cont
.empty()) {
130 outbuf_cont
= " {} ";
132 uint32_t response_length
= htonl(outbuf_cont
.length());
133 logger().info("asok response length: {}", outbuf_cont
.length());
135 return out
.write((char*)&response_length
, sizeof(uint32_t))
136 .then([&out
, outbuf_cont
] { return out
.write(outbuf_cont
.c_str()); });
139 seastar::future
<> AdminSocket::execute_line(std::string cmdline
,
140 seastar::output_stream
<char>& out
)
142 return seastar::with_shared(servers_tbl_rwlock
,
143 [this, cmdline
, &out
]() mutable {
144 ceph::bufferlist err
;
145 auto parsed
= parse_cmd(cmdline
, err
);
146 if (!parsed
.has_value() ||
147 !validate_command(*parsed
, cmdline
, err
)) {
148 return finalize_response(out
, std::move(err
));
150 return parsed
->hook
.call(parsed
->hook
.prefix
,
152 parsed
->parameters
).then(
153 [this, &out
](auto result
) {
154 // add 'failed' to the contents of out_buf? not what
155 // happens in the old code
156 return finalize_response(out
, std::move(result
));
161 // an input_stream consumer that reads buffer into a std::string up to the first
162 // '\0' which indicates the end of command
163 struct line_consumer
{
164 using tmp_buf
= seastar::temporary_buffer
<char>;
165 using consumption_result_type
=
166 typename
seastar::input_stream
<char>::consumption_result_type
;
168 seastar::future
<consumption_result_type
> operator()(tmp_buf
&& buf
) {
173 buf
.trim_front(consumed
);
174 return seastar::make_ready_future
<consumption_result_type
>(
175 consumption_result_type::stop_consuming_type(std::move(buf
)));
180 return seastar::make_ready_future
<consumption_result_type
>(
181 seastar::continue_consuming
{});
186 seastar::future
<> AdminSocket::handle_client(seastar::input_stream
<char>& in
,
187 seastar::output_stream
<char>& out
)
189 auto consumer
= seastar::make_shared
<line_consumer
>();
190 return in
.consume(*consumer
).then([consumer
, &out
, this] {
191 logger().debug("AdminSocket::handle_client: incoming asok string: {}",
193 return execute_line(consumer
->line
, out
);
200 }).handle_exception([](auto ep
) {
201 logger().debug("exception on {}: {}", __func__
, ep
);
202 return seastar::make_ready_future
<>();
206 seastar::future
<> AdminSocket::start(const std::string
& path
)
210 "{}: Admin Socket socket path missing from the configuration", __func__
);
211 return seastar::now();
214 logger().debug("{}: asok socket path={}", __func__
, path
);
215 auto sock_path
= seastar::socket_address
{ seastar::unix_domain_addr
{ path
} };
216 server_sock
= seastar::engine().listen(sock_path
);
217 // listen in background
218 std::ignore
= seastar::do_until(
219 [this] { return stop_gate
.is_closed(); },
221 return seastar::with_gate(stop_gate
, [this] {
222 assert(!connected_sock
.has_value());
223 return server_sock
->accept().then([this](seastar::accept_result acc
) {
224 connected_sock
= std::move(acc
.connection
);
225 return seastar::do_with(connected_sock
->input(),
226 connected_sock
->output(),
227 [this](auto& input
, auto& output
) mutable {
228 return handle_client(input
, output
);
230 assert(connected_sock
.has_value());
231 connected_sock
.reset();
233 }).handle_exception([this](auto ep
) {
234 if (!stop_gate
.is_closed()) {
235 logger().error("AdminSocket: terminated: {}", ep
);
240 logger().debug("AdminSocket::init(): admin-sock thread terminated");
241 return seastar::now();
244 return seastar::make_ready_future
<>();
247 seastar::future
<> AdminSocket::stop()
250 return seastar::now();
252 server_sock
->abort_accept();
254 if (connected_sock
) {
255 connected_sock
->shutdown_input();
256 connected_sock
->shutdown_output();
257 connected_sock
.reset();
259 return stop_gate
.close();
262 /////////////////////////////////////////
263 // the internal hooks
264 /////////////////////////////////////////
266 class VersionHook final
: public AdminSocketHook
{
269 : AdminSocketHook
{"version", "version", "get ceph version"}
271 seastar::future
<bufferlist
> call(std::string_view
,
272 std::string_view format
,
273 const cmdmap_t
&) const final
275 unique_ptr
<Formatter
> f
{Formatter::create(format
, "json-pretty", "json-pretty")};
276 f
->open_object_section("version");
277 f
->dump_string("version", ceph_version_to_str());
278 f
->dump_string("release", ceph_release_to_str());
279 f
->dump_string("release_type", ceph_release_type());
283 return seastar::make_ready_future
<bufferlist
>(std::move(out
));
288 Note that the git_version command is expected to return a 'version' JSON
291 class GitVersionHook final
: public AdminSocketHook
{
294 : AdminSocketHook
{"git_version", "git_version", "get git sha1"}
296 seastar::future
<bufferlist
> call(std::string_view command
,
297 std::string_view format
,
298 const cmdmap_t
&) const final
300 unique_ptr
<Formatter
> f
{Formatter::create(format
, "json-pretty", "json-pretty")};
301 f
->open_object_section("version");
302 f
->dump_string("git_version", git_version_to_str());
304 ceph::bufferlist out
;
306 return seastar::make_ready_future
<bufferlist
>(std::move(out
));
310 class HelpHook final
: public AdminSocketHook
{
311 const AdminSocket
& m_as
;
314 explicit HelpHook(const AdminSocket
& as
) :
315 AdminSocketHook
{"help", "help", "list available commands"},
319 seastar::future
<bufferlist
> call(std::string_view command
,
320 std::string_view format
,
321 const cmdmap_t
& cmdmap
) const final
323 return seastar::with_shared(m_as
.servers_tbl_rwlock
,
325 unique_ptr
<Formatter
> f
{Formatter::create(format
, "json-pretty", "json-pretty")};
326 f
->open_object_section("help");
327 for (const auto& [prefix
, hook
] : m_as
) {
328 if (!hook
->help
.empty()) {
329 f
->dump_string(prefix
.data(), hook
->help
);
333 ceph::bufferlist out
;
335 return seastar::make_ready_future
<bufferlist
>(std::move(out
));
340 class GetdescsHook final
: public AdminSocketHook
{
341 const AdminSocket
& m_as
;
344 explicit GetdescsHook(const AdminSocket
& as
) :
345 AdminSocketHook
{"get_command_descriptions",
346 "get_command_descriptions",
347 "list available commands"},
350 seastar::future
<bufferlist
> call(std::string_view command
,
351 std::string_view format
,
352 const cmdmap_t
& cmdmap
) const final
354 unique_ptr
<Formatter
> f
{Formatter::create(format
, "json-pretty", "json-pretty")};
355 return seastar::with_shared(m_as
.servers_tbl_rwlock
, [this, f
=move(f
)] {
357 f
->open_object_section("command_descriptions");
358 for (const auto& [prefix
, hook
] : m_as
) {
359 auto secname
= fmt::format("cmd {:>03}", cmdnum
);
360 dump_cmd_and_help_to_json(f
.get(), CEPH_FEATURES_ALL
, secname
,
361 std::string
{hook
->desc
},
362 std::string
{hook
->help
});
366 ceph::bufferlist out
;
368 return seastar::make_ready_future
<bufferlist
>(std::move(out
));
373 /// the hooks that are served directly by the admin_socket server
374 seastar::future
<> AdminSocket::register_admin_commands()
376 return seastar::when_all_succeed(
377 register_command(std::make_unique
<VersionHook
>()),
378 register_command(std::make_unique
<GitVersionHook
>()),
379 register_command(std::make_unique
<HelpHook
>(*this)),
380 register_command(std::make_unique
<GetdescsHook
>(*this)));
383 } // namespace crimson::admin