]> git.proxmox.com Git - ceph.git/blob - ceph/src/test/admin_socket.cc
69bbcedb3cd4af8a2ac52be2c4d965e02f2ddc96
[ceph.git] / ceph / src / test / admin_socket.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3 /*
4 * Ceph - scalable distributed file system
5 *
6 * Copyright (C) 2011 New Dream Network
7 *
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
12 *
13 */
14
15 #include "common/ceph_mutex.h"
16 #include "common/Cond.h"
17 #include "common/admin_socket.h"
18 #include "common/admin_socket_client.h"
19 #include "common/ceph_argparse.h"
20 #include "gtest/gtest.h"
21
22 #include <stdint.h>
23 #include <string.h>
24 #include <string>
25 #include <sys/un.h>
26
27 using namespace std;
28
29 class AdminSocketTest
30 {
31 public:
32 explicit AdminSocketTest(AdminSocket *asokc)
33 : m_asokc(asokc)
34 {
35 }
36 bool init(const std::string &uri) {
37 return m_asokc->init(uri);
38 }
39 string bind_and_listen(const std::string &sock_path, int *fd) {
40 return m_asokc->bind_and_listen(sock_path, fd);
41 }
42 bool shutdown() {
43 m_asokc->shutdown();
44 return true;
45 }
46 AdminSocket *m_asokc;
47 };
48
49 TEST(AdminSocket, Teardown) {
50 std::unique_ptr<AdminSocket> asokc = std::make_unique<AdminSocket>(g_ceph_context);
51 AdminSocketTest asoct(asokc.get());
52 ASSERT_EQ(true, asoct.shutdown());
53 }
54
55 TEST(AdminSocket, TeardownSetup) {
56 std::unique_ptr<AdminSocket> asokc = std::make_unique<AdminSocket>(g_ceph_context);
57 AdminSocketTest asoct(asokc.get());
58 ASSERT_EQ(true, asoct.shutdown());
59 ASSERT_EQ(true, asoct.init(get_rand_socket_path()));
60 ASSERT_EQ(true, asoct.shutdown());
61 }
62
63 TEST(AdminSocket, SendHelp) {
64 std::unique_ptr<AdminSocket> asokc = std::make_unique<AdminSocket>(g_ceph_context);
65 AdminSocketTest asoct(asokc.get());
66 ASSERT_EQ(true, asoct.shutdown());
67 ASSERT_EQ(true, asoct.init(get_rand_socket_path()));
68 AdminSocketClient client(get_rand_socket_path());
69
70 {
71 string help;
72 ASSERT_EQ("", client.do_request("{\"prefix\":\"help\"}", &help));
73 ASSERT_NE(string::npos, help.find("\"list available commands\""));
74 }
75 {
76 string help;
77 ASSERT_EQ("", client.do_request("{"
78 " \"prefix\":\"help\","
79 " \"format\":\"xml\","
80 "}", &help));
81 ASSERT_NE(string::npos, help.find(">list available commands<"));
82 }
83 {
84 string help;
85 ASSERT_EQ("", client.do_request("{"
86 " \"prefix\":\"help\","
87 " \"format\":\"UNSUPPORTED\","
88 "}", &help));
89 ASSERT_NE(string::npos, help.find("\"list available commands\""));
90 }
91 ASSERT_EQ(true, asoct.shutdown());
92 }
93
94 TEST(AdminSocket, SendNoOp) {
95 std::unique_ptr<AdminSocket> asokc = std::make_unique<AdminSocket>(g_ceph_context);
96 AdminSocketTest asoct(asokc.get());
97 ASSERT_EQ(true, asoct.shutdown());
98 ASSERT_EQ(true, asoct.init(get_rand_socket_path()));
99 AdminSocketClient client(get_rand_socket_path());
100 string version;
101 ASSERT_EQ("", client.do_request("{\"prefix\":\"0\"}", &version));
102 ASSERT_EQ(CEPH_ADMIN_SOCK_VERSION, version);
103 ASSERT_EQ(true, asoct.shutdown());
104 }
105
106 TEST(AdminSocket, SendTooLongRequest) {
107 std::unique_ptr<AdminSocket> asokc = std::make_unique<AdminSocket>(g_ceph_context);
108 AdminSocketTest asoct(asokc.get());
109 ASSERT_EQ(true, asoct.shutdown());
110 ASSERT_EQ(true, asoct.init(get_rand_socket_path()));
111 AdminSocketClient client(get_rand_socket_path());
112 string version;
113 string request(16384, 'a');
114 //if admin_socket cannot handle it, segfault will happened.
115 ASSERT_NE("", client.do_request(request, &version));
116 ASSERT_EQ(true, asoct.shutdown());
117 }
118
119 class MyTest : public AdminSocketHook {
120 int call(std::string_view command, const cmdmap_t& cmdmap,
121 Formatter *f,
122 std::ostream& ss,
123 bufferlist& result) override {
124 std::vector<std::string> args;
125 TOPNSPC::common::cmd_getval(cmdmap, "args", args);
126 result.append(command);
127 result.append("|");
128 string resultstr;
129 for (std::vector<std::string>::iterator it = args.begin();
130 it != args.end(); ++it) {
131 if (it != args.begin())
132 resultstr += ' ';
133 resultstr += *it;
134 }
135 result.append(resultstr);
136 return 0;
137 }
138 };
139
140 TEST(AdminSocket, RegisterCommand) {
141 std::unique_ptr<AdminSocket> asokc = std::make_unique<AdminSocket>(g_ceph_context);
142 std::unique_ptr<AdminSocketHook> my_test_asok = std::make_unique<MyTest>();
143 AdminSocketTest asoct(asokc.get());
144 ASSERT_EQ(true, asoct.shutdown());
145 ASSERT_EQ(true, asoct.init(get_rand_socket_path()));
146 AdminSocketClient client(get_rand_socket_path());
147 ASSERT_EQ(0, asoct.m_asokc->register_command("test", my_test_asok.get(), ""));
148 string result;
149 ASSERT_EQ("", client.do_request("{\"prefix\":\"test\"}", &result));
150 ASSERT_EQ("test|", result);
151 ASSERT_EQ(true, asoct.shutdown());
152 }
153
154 class MyTest2 : public AdminSocketHook {
155 int call(std::string_view command, const cmdmap_t& cmdmap,
156 Formatter *f,
157 std::ostream& ss,
158 bufferlist& result) override {
159 std::vector<std::string> args;
160 TOPNSPC::common::cmd_getval(cmdmap, "args", args);
161 result.append(command);
162 result.append("|");
163 string resultstr;
164 for (std::vector<std::string>::iterator it = args.begin();
165 it != args.end(); ++it) {
166 if (it != args.begin())
167 resultstr += ' ';
168 resultstr += *it;
169 }
170 result.append(resultstr);
171 ss << "error stream";
172 return 0;
173 }
174 };
175
176 TEST(AdminSocket, RegisterCommandPrefixes) {
177 std::unique_ptr<AdminSocket> asokc = std::make_unique<AdminSocket>(g_ceph_context);
178 std::unique_ptr<AdminSocketHook> my_test_asok = std::make_unique<MyTest>();
179 std::unique_ptr<AdminSocketHook> my_test2_asok = std::make_unique<MyTest2>();
180 AdminSocketTest asoct(asokc.get());
181 ASSERT_EQ(true, asoct.shutdown());
182 ASSERT_EQ(true, asoct.init(get_rand_socket_path()));
183 AdminSocketClient client(get_rand_socket_path());
184 ASSERT_EQ(0, asoct.m_asokc->register_command("test name=args,type=CephString,n=N", my_test_asok.get(), ""));
185 ASSERT_EQ(0, asoct.m_asokc->register_command("test command name=args,type=CephString,n=N", my_test2_asok.get(), ""));
186 string result;
187 ASSERT_EQ("", client.do_request("{\"prefix\":\"test\"}", &result));
188 ASSERT_EQ("test|", result);
189 ASSERT_EQ("", client.do_request("{\"prefix\":\"test command\"}", &result));
190 ASSERT_EQ("test command|", result);
191 ASSERT_EQ("", client.do_request("{\"prefix\":\"test command\",\"args\":[\"post\"]}", &result));
192 ASSERT_EQ("test command|post", result);
193 ASSERT_EQ("", client.do_request("{\"prefix\":\"test command\",\"args\":[\" post\"]}", &result));
194 ASSERT_EQ("test command| post", result);
195 ASSERT_EQ("", client.do_request("{\"prefix\":\"test\",\"args\":[\"this thing\"]}", &result));
196 ASSERT_EQ("test|this thing", result);
197
198 ASSERT_EQ("", client.do_request("{\"prefix\":\"test\",\"args\":[\" command post\"]}", &result));
199 ASSERT_EQ("test| command post", result);
200 ASSERT_EQ("", client.do_request("{\"prefix\":\"test\",\"args\":[\" this thing\"]}", &result));
201 ASSERT_EQ("test| this thing", result);
202 ASSERT_EQ(true, asoct.shutdown());
203 }
204
205 class BlockingHook : public AdminSocketHook {
206 public:
207 ceph::mutex _lock = ceph::make_mutex("BlockingHook::_lock");
208 ceph::condition_variable _cond;
209
210 BlockingHook() = default;
211
212 int call(std::string_view command, const cmdmap_t& cmdmap,
213 Formatter *f,
214 std::ostream& ss,
215 bufferlist& result) override {
216 std::unique_lock l{_lock};
217 _cond.wait(l);
218 return 0;
219 }
220 };
221
222 TEST(AdminSocketClient, Ping) {
223 string path = get_rand_socket_path();
224 std::unique_ptr<AdminSocket> asokc = std::make_unique<AdminSocket>(g_ceph_context);
225 AdminSocketClient client(path);
226 // no socket
227 {
228 bool ok;
229 std::string result = client.ping(&ok);
230 #ifndef _WIN32
231 // TODO: convert WSA errors.
232 EXPECT_NE(std::string::npos, result.find("No such file or directory"));
233 #endif
234 ASSERT_FALSE(ok);
235 }
236 // file exists but does not allow connections (no process, wrong type...)
237 int fd = ::creat(path.c_str(), 0777);
238 ASSERT_TRUE(fd);
239 // On Windows, we won't be able to remove the file unless we close it
240 // first.
241 ASSERT_FALSE(::close(fd));
242 {
243 bool ok;
244 std::string result = client.ping(&ok);
245 #ifndef _WIN32
246 #if defined(__APPLE__) || defined(__FreeBSD__)
247 const char* errmsg = "Socket operation on non-socket";
248 #else
249 const char* errmsg = "Connection refused";
250 #endif
251 EXPECT_NE(std::string::npos, result.find(errmsg));
252 #endif /* _WIN32 */
253 ASSERT_FALSE(ok);
254 }
255 // a daemon is connected to the socket
256 {
257 AdminSocketTest asoct(asokc.get());
258 ASSERT_TRUE(asoct.init(path));
259 bool ok;
260 std::string result = client.ping(&ok);
261 EXPECT_EQ("", result);
262 ASSERT_TRUE(ok);
263 ASSERT_TRUE(asoct.shutdown());
264 }
265 // hardcoded five seconds timeout prevents infinite blockage
266 {
267 AdminSocketTest asoct(asokc.get());
268 BlockingHook *blocking = new BlockingHook();
269 ASSERT_EQ(0, asoct.m_asokc->register_command("0", blocking, ""));
270 ASSERT_TRUE(asoct.init(path));
271 bool ok;
272 std::string result = client.ping(&ok);
273 #ifndef _WIN32
274 EXPECT_NE(std::string::npos, result.find("Resource temporarily unavailable"));
275 #endif
276 ASSERT_FALSE(ok);
277 {
278 std::lock_guard l{blocking->_lock};
279 blocking->_cond.notify_all();
280 }
281 ASSERT_TRUE(asoct.shutdown());
282 delete blocking;
283 }
284 }
285
286 TEST(AdminSocket, bind_and_listen) {
287 string path = get_rand_socket_path();
288 std::unique_ptr<AdminSocket> asokc = std::make_unique<AdminSocket>(g_ceph_context);
289
290 AdminSocketTest asoct(asokc.get());
291 // successfull bind
292 {
293 int fd = 0;
294 string message;
295 message = asoct.bind_and_listen(path, &fd);
296 ASSERT_NE(0, fd);
297 ASSERT_EQ("", message);
298 ASSERT_EQ(0, ::compat_closesocket(fd));
299 ASSERT_EQ(0, ::unlink(path.c_str()));
300 }
301 // silently discard an existing file
302 {
303 int fd = 0;
304 string message;
305 int fd2 = ::creat(path.c_str(), 0777);
306 ASSERT_TRUE(fd2);
307 // On Windows, we won't be able to remove the file unless we close it
308 // first.
309 ASSERT_FALSE(::close(fd2));
310 message = asoct.bind_and_listen(path, &fd);
311 ASSERT_NE(0, fd);
312 ASSERT_EQ("", message);
313 ASSERT_EQ(0, ::compat_closesocket(fd));
314 ASSERT_EQ(0, ::unlink(path.c_str()));
315 }
316 // do not take over a live socket
317 {
318 ASSERT_TRUE(asoct.init(path));
319 int fd = 0;
320 string message;
321 message = asoct.bind_and_listen(path, &fd);
322 std::cout << "message: " << message << std::endl;
323 EXPECT_NE(std::string::npos, message.find("File exists"));
324 ASSERT_TRUE(asoct.shutdown());
325 }
326 }
327
328 /*
329 * Local Variables:
330 * compile-command: "cd .. ;
331 * make unittest_admin_socket &&
332 * valgrind \
333 * --max-stackframe=20000000 --tool=memcheck \
334 * ./unittest_admin_socket --debug-asok 20 # --gtest_filter=AdminSocket*.*
335 * "
336 * End:
337 */
338