]>
Commit | Line | Data |
---|---|---|
9f95a23c TL |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | #pragma once | |
4 | ||
5 | /** | |
6 | A Crimson-wise version of the src/common/admin_socket.h | |
7 | ||
8 | Note: assumed to be running on a single core. | |
9 | */ | |
10 | #include <map> | |
11 | #include <string> | |
12 | #include <string_view> | |
13 | ||
14 | #include <seastar/core/future.hh> | |
15 | #include <seastar/core/gate.hh> | |
16 | #include <seastar/core/iostream.hh> | |
17 | #include <seastar/core/shared_mutex.hh> | |
18 | #include <seastar/core/shared_ptr.hh> | |
19 | #include <seastar/net/api.hh> | |
20 | ||
21 | #include "common/cmdparse.h" | |
22 | ||
23 | using namespace std::literals; | |
24 | ||
25 | namespace crimson::admin { | |
26 | ||
27 | class AdminSocket; | |
28 | ||
29 | /** | |
30 | * A specific hook must implement exactly one of the two interfaces: | |
31 | * (1) call(command, cmdmap, format, out) | |
32 | * or | |
33 | * (2) exec_command(formatter, command, cmdmap, format, out) | |
34 | * | |
35 | * The default implementation of (1) above calls exec_command() after handling | |
36 | * most of the boiler-plate choirs: | |
37 | * - setting up the formatter, with an appropiate 'section' already opened; | |
38 | * - handling possible failures (exceptions or future_exceptions) returned | |
39 | * by (2) | |
40 | * - flushing the output to the outgoing bufferlist. | |
41 | */ | |
42 | class AdminSocketHook { | |
43 | public: | |
44 | AdminSocketHook(std::string_view prefix, | |
45 | std::string_view desc, | |
46 | std::string_view help) : | |
47 | prefix{prefix}, desc{desc}, help{help} | |
48 | {} | |
49 | /** | |
50 | * \retval 'false' for hook execution errors | |
51 | */ | |
52 | virtual seastar::future<ceph::bufferlist> | |
53 | call(std::string_view command, | |
54 | std::string_view format, | |
55 | const cmdmap_t& cmdmap) const = 0; | |
56 | virtual ~AdminSocketHook() {} | |
57 | const std::string_view prefix; | |
58 | const std::string_view desc; | |
59 | const std::string_view help; | |
60 | }; | |
61 | ||
62 | class AdminSocket : public seastar::enable_lw_shared_from_this<AdminSocket> { | |
63 | public: | |
64 | AdminSocket() = default; | |
65 | ~AdminSocket() = default; | |
66 | ||
67 | AdminSocket(const AdminSocket&) = delete; | |
68 | AdminSocket& operator=(const AdminSocket&) = delete; | |
69 | AdminSocket(AdminSocket&&) = delete; | |
70 | AdminSocket& operator=(AdminSocket&&) = delete; | |
71 | ||
72 | using hook_server_tag = const void*; | |
73 | ||
74 | /** | |
75 | * create the async Seastar thread that handles asok commands arriving | |
76 | * over the socket. | |
77 | */ | |
78 | seastar::future<> start(const std::string& path); | |
79 | ||
80 | seastar::future<> stop(); | |
81 | ||
82 | /** | |
83 | * register an admin socket hooks server | |
84 | * | |
85 | * The server registers a set of APIs under a common hook_server_tag. | |
86 | * | |
87 | * Commands (APIs) are registered under a command string. Incoming | |
88 | * commands are split by spaces and matched against the longest | |
89 | * registered command. For example, if 'foo' and 'foo bar' are | |
90 | * registered, and an incoming command is 'foo bar baz', it is | |
91 | * matched with 'foo bar', while 'foo fud' will match 'foo'. | |
92 | * | |
93 | * The entire incoming command string is passed to the registered | |
94 | * hook. | |
95 | * | |
96 | * \param server_tag a tag identifying the server registering the hook | |
97 | * \param apis_served a vector of the commands served by this server. Each | |
98 | * command registration includes its identifying command string, the | |
99 | * expected call syntax, and some help text. | |
100 | * | |
101 | * A note regarding the help text: if empty, command will not be | |
102 | * included in 'help' output. | |
103 | * | |
104 | * \retval a shared ptr to the asok server itself, or nullopt if | |
105 | * a block with same tag is already registered. | |
106 | */ | |
107 | seastar::future<> register_command(std::unique_ptr<AdminSocketHook>&& hook); | |
108 | ||
109 | /** | |
110 | * Registering the APIs that are served directly by the admin_socket server. | |
111 | */ | |
112 | seastar::future<> register_admin_commands(); | |
113 | ||
114 | private: | |
115 | /** | |
116 | * the result of analyzing an incoming command, and locating it in | |
117 | * the registered APIs collection. | |
118 | */ | |
119 | struct parsed_command_t { | |
120 | cmdmap_t parameters; | |
121 | std::string format; | |
122 | const AdminSocketHook& hook; | |
123 | }; | |
124 | // and the shorthand: | |
125 | using maybe_parsed_t = std::optional<AdminSocket::parsed_command_t>; | |
126 | ||
127 | seastar::future<> handle_client(seastar::input_stream<char>& inp, | |
128 | seastar::output_stream<char>& out); | |
129 | ||
130 | seastar::future<> execute_line(std::string cmdline, | |
131 | seastar::output_stream<char>& out); | |
132 | ||
133 | seastar::future<> finalize_response(seastar::output_stream<char>& out, | |
134 | ceph::bufferlist&& msgs); | |
135 | ||
136 | bool validate_command(const parsed_command_t& parsed, | |
137 | const std::string& command_text, | |
138 | ceph::bufferlist& out) const; | |
139 | ||
140 | std::optional<seastar::server_socket> server_sock; | |
141 | std::optional<seastar::connected_socket> connected_sock; | |
142 | ||
143 | /** | |
144 | * stopping incoming ASOK requests at shutdown | |
145 | */ | |
146 | seastar::gate stop_gate; | |
147 | ||
148 | /** | |
149 | * parse the incoming command line into the sequence of words that identifies | |
150 | * the API, and into its arguments. Locate the command string in the | |
151 | * registered blocks. | |
152 | */ | |
153 | maybe_parsed_t parse_cmd(std::string command_text, bufferlist& out); | |
154 | ||
155 | /** | |
156 | * The servers table is protected by a rw-lock, to be acquired exclusively | |
157 | * only when registering or removing a server. | |
158 | * The lock is locked-shared when executing any hook. | |
159 | */ | |
160 | mutable seastar::shared_mutex servers_tbl_rwlock; | |
161 | using hooks_t = std::map<std::string_view, std::unique_ptr<AdminSocketHook>>; | |
162 | hooks_t hooks; | |
163 | ||
164 | public: | |
165 | /** | |
166 | * iterator support | |
167 | */ | |
168 | hooks_t::const_iterator begin() const { | |
169 | return hooks.cbegin(); | |
170 | } | |
171 | hooks_t::const_iterator end() const { | |
172 | return hooks.cend(); | |
173 | } | |
174 | ||
175 | friend class AdminSocketTest; | |
176 | friend class HelpHook; | |
177 | friend class GetdescsHook; | |
178 | }; | |
179 | ||
180 | } // namespace crimson::admin |