]> git.proxmox.com Git - ceph.git/blame - ceph/src/test/librados/test.cc
add subtree-ish sources for 12.0.3
[ceph.git] / ceph / src / test / librados / test.cc
CommitLineData
7c673cae
FG
1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*
2// vim: ts=8 sw=2 smarttab
3
4#include "include/rados/librados.h"
5#include "include/rados/librados.hpp"
6#include "test/librados/test.h"
7
8#include "include/stringify.h"
9#include "common/Formatter.h"
10#include "json_spirit/json_spirit.h"
11#include "common/errno.h"
12#include "common/ceph_context.h"
13#include "common/config.h"
14
15#include <errno.h>
16#include <sstream>
17#include <stdlib.h>
18#include <string>
19#include <time.h>
20#include <unistd.h>
21#include <iostream>
22#include "gtest/gtest.h"
23
24using namespace librados;
25
26std::string get_temp_pool_name(const std::string &prefix)
27{
28 char hostname[80];
29 char out[160];
30 memset(hostname, 0, sizeof(hostname));
31 memset(out, 0, sizeof(out));
32 gethostname(hostname, sizeof(hostname)-1);
33 static int num = 1;
34 snprintf(out, sizeof(out), "%s-%d-%d", hostname, getpid(), num);
35 num++;
36 return prefix + out;
37}
38
39int wait_for_healthy(rados_t *cluster)
40{
41 bool healthy = false;
42 // This timeout is very long because the tests are sometimes
43 // run on a thrashing cluster
44 int timeout = 3600;
45 int slept = 0;
46
47 while(!healthy) {
48 JSONFormatter cmd_f;
49 cmd_f.open_object_section("command");
50 cmd_f.dump_string("prefix", "status");
51 cmd_f.dump_string("format", "json");
52 cmd_f.close_section();
53 std::ostringstream cmd_stream;
54 cmd_f.flush(cmd_stream);
55 const std::string serialized_cmd = cmd_stream.str();
56
57 const char *cmd[2];
58 cmd[1] = NULL;
59 cmd[0] = serialized_cmd.c_str();
60
61 char *outbuf = NULL;
62 size_t outlen = 0;
63 int ret = rados_mon_command(*cluster, (const char **)cmd, 1, "", 0,
64 &outbuf, &outlen, NULL, NULL);
65 if (ret) {
66 return ret;
67 }
68
69 std::string out(outbuf, outlen);
70 rados_buffer_free(outbuf);
71
72 json_spirit::mValue root;
73 assert(json_spirit::read(out, root));
74 json_spirit::mObject root_obj = root.get_obj();
75 json_spirit::mObject pgmap = root_obj["pgmap"].get_obj();
76 json_spirit::mArray pgs_by_state = pgmap["pgs_by_state"].get_array();
77
78 if (pgs_by_state.size() == 1) {
79 json_spirit::mObject state = pgs_by_state[0].get_obj();
80 std::string state_name = state["state_name"].get_str();
81 if (state_name != std::string("active+clean")) {
82 healthy = false;
83 } else {
84 healthy = true;
85 }
86
87 } else {
88 healthy = false;
89 }
90
91 if (slept >= timeout) {
92 return -ETIMEDOUT;
93 };
94
95 if (!healthy) {
96 sleep(1);
97 slept += 1;
98 }
99 }
100
101 return 0;
102}
103
104int rados_pool_set(
105 rados_t *cluster,
106 const std::string &pool_name,
107 const std::string &var,
108 const std::string &val)
109{
110 JSONFormatter cmd_f;
111 cmd_f.open_object_section("command");
112 cmd_f.dump_string("prefix", "osd pool set");
113 cmd_f.dump_string("pool", pool_name);
114 cmd_f.dump_string("var", var);
115 cmd_f.dump_string("val", val);
116 cmd_f.close_section();
117
118 std::ostringstream cmd_stream;
119 cmd_f.flush(cmd_stream);
120
121 const std::string serialized_cmd = cmd_stream.str();
122
123 const char *cmd[2];
124 cmd[1] = NULL;
125 cmd[0] = serialized_cmd.c_str();
126 int ret = rados_mon_command(*cluster, (const char **)cmd, 1, "", 0, NULL,
127 NULL, NULL, NULL);
128 return ret;
129}
130
131std::string create_one_pool(
132 const std::string &pool_name, rados_t *cluster, uint32_t pg_num)
133{
134 std::string err_str = connect_cluster(cluster);
135 if (err_str.length())
136 return err_str;
137
138 int ret = rados_pool_create(*cluster, pool_name.c_str());
139 if (ret) {
140 rados_shutdown(*cluster);
141 std::ostringstream oss;
142 oss << "create_one_pool(" << pool_name << ") failed with error " << ret;
143 return oss.str();
144 }
145
146 return "";
147}
148
149int destroy_ec_profile(rados_t *cluster,
150 const std::string& pool_name,
151 std::ostream &oss)
152{
153 char buf[1000];
154 snprintf(buf, sizeof(buf),
155 "{\"prefix\": \"osd erasure-code-profile rm\", \"name\": \"testprofile-%s\"}",
156 pool_name.c_str());
157 char *cmd[2];
158 cmd[0] = buf;
159 cmd[1] = NULL;
160 int ret = rados_mon_command(*cluster, (const char **)cmd, 1, "", 0, NULL,
161 0, NULL, 0);
162 if (ret)
163 oss << "rados_mon_command: erasure-code-profile rm testprofile-"
164 << pool_name << " failed with error " << ret;
165 return ret;
166}
167
168int destroy_ruleset(rados_t *cluster,
169 std::string ruleset,
170 std::ostream &oss)
171{
172 char *cmd[2];
173 std::string tmp = ("{\"prefix\": \"osd crush rule rm\", \"name\":\"" +
174 ruleset + "\"}");
175 cmd[0] = (char*)tmp.c_str();
176 cmd[1] = NULL;
177 int ret = rados_mon_command(*cluster, (const char **)cmd, 1, "", 0, NULL, 0, NULL, 0);
178 if (ret)
179 oss << "rados_mon_command: osd crush rule rm " + ruleset + " failed with error " << ret;
180 return ret;
181}
182
183int destroy_ec_profile_and_ruleset(rados_t *cluster,
184 std::string ruleset,
185 std::ostream &oss)
186{
187 int ret;
188 ret = destroy_ec_profile(cluster, ruleset, oss);
189 if (ret)
190 return ret;
191 return destroy_ruleset(cluster, ruleset, oss);
192}
193
194std::string set_pg_num(
195 rados_t *cluster, const std::string &pool_name, uint32_t pg_num)
196{
197 // Wait for 'creating' to clear
198 int r = wait_for_healthy(cluster);
199 if (r != 0) {
200 goto err;
201 }
202
203 // Adjust pg_num
204 r = rados_pool_set(cluster, pool_name, "pg_num", stringify(pg_num));
205 if (r != 0) {
206 goto err;
207 }
208
209 // Wait for 'creating' to clear
210 r = wait_for_healthy(cluster);
211 if (r != 0) {
212 goto err;
213 }
214
215 return "";
216
217err:
218 rados_shutdown(*cluster);
219 std::ostringstream oss;
220 oss << __func__ << "(" << pool_name << ") failed with error " << r;
221 return oss.str();
222}
223
224std::string create_one_ec_pool(const std::string &pool_name, rados_t *cluster)
225{
226 std::string err = connect_cluster(cluster);
227 if (err.length())
228 return err;
229
230 std::ostringstream oss;
231 int ret = destroy_ec_profile_and_ruleset(cluster, pool_name, oss);
232 if (ret) {
233 rados_shutdown(*cluster);
234 return oss.str();
235 }
236
237 char *cmd[2];
238 cmd[1] = NULL;
239
240 std::string profile_create = "{\"prefix\": \"osd erasure-code-profile set\", \"name\": \"testprofile-" + pool_name + "\", \"profile\": [ \"k=2\", \"m=1\", \"ruleset-failure-domain=osd\"]}";
241 cmd[0] = (char *)profile_create.c_str();
242 ret = rados_mon_command(*cluster, (const char **)cmd, 1, "", 0, NULL, 0, NULL, 0);
243 if (ret) {
244 rados_shutdown(*cluster);
245 oss << "rados_mon_command erasure-code-profile set name:testprofile-" << pool_name << " failed with error " << ret;
246 return oss.str();
247 }
248
249 std::string cmdstr = "{\"prefix\": \"osd pool create\", \"pool\": \"" +
250 pool_name + "\", \"pool_type\":\"erasure\", \"pg_num\":8, \"pgp_num\":8, \"erasure_code_profile\":\"testprofile-" + pool_name + "\"}";
251 cmd[0] = (char *)cmdstr.c_str();
252 ret = rados_mon_command(*cluster, (const char **)cmd, 1, "", 0, NULL, 0, NULL, 0);
253 if (ret) {
254 destroy_ec_profile(cluster, pool_name, oss);
255 rados_shutdown(*cluster);
256 oss << "rados_mon_command osd pool create failed with error " << ret;
257 return oss.str();
258 }
259
260 rados_wait_for_latest_osdmap(*cluster);
261 return "";
262}
263
264std::string create_one_pool_pp(const std::string &pool_name, Rados &cluster)
265{
266 return create_one_pool_pp(pool_name, cluster, {});
267}
268std::string create_one_pool_pp(const std::string &pool_name, Rados &cluster,
269 const std::map<std::string, std::string> &config)
270{
271 std::string err = connect_cluster_pp(cluster, config);
272 if (err.length())
273 return err;
274 int ret = cluster.pool_create(pool_name.c_str());
275 if (ret) {
276 cluster.shutdown();
277 std::ostringstream oss;
278 oss << "cluster.pool_create(" << pool_name << ") failed with error " << ret;
279 return oss.str();
280 }
281 return "";
282}
283
284int destroy_ruleset_pp(Rados &cluster,
285 std::string ruleset,
286 std::ostream &oss)
287{
288 bufferlist inbl;
289 int ret = cluster.mon_command("{\"prefix\": \"osd crush rule rm\", \"name\":\"" +
290 ruleset + "\"}", inbl, NULL, NULL);
291 if (ret)
292 oss << "mon_command: osd crush rule rm " + ruleset + " failed with error " << ret << std::endl;
293 return ret;
294}
295
296int destroy_ec_profile_pp(Rados &cluster, const std::string& pool_name,
297 std::ostream &oss)
298{
299 bufferlist inbl;
300 int ret = cluster.mon_command("{\"prefix\": \"osd erasure-code-profile rm\", \"name\": \"testprofile-" + pool_name + "\"}",
301 inbl, NULL, NULL);
302 if (ret)
303 oss << "mon_command: osd erasure-code-profile rm testprofile-" << pool_name << " failed with error " << ret << std::endl;
304 return ret;
305}
306
307int destroy_ec_profile_and_ruleset_pp(Rados &cluster,
308 std::string ruleset,
309 std::ostream &oss)
310{
311 int ret;
312 ret = destroy_ec_profile_pp(cluster, ruleset, oss);
313 if (ret)
314 return ret;
315 return destroy_ruleset_pp(cluster, ruleset, oss);
316}
317
318std::string create_one_ec_pool_pp(const std::string &pool_name, Rados &cluster)
319{
320 std::string err = connect_cluster_pp(cluster);
321 if (err.length())
322 return err;
323
324 std::ostringstream oss;
325 int ret = destroy_ec_profile_and_ruleset_pp(cluster, pool_name, oss);
326 if (ret) {
327 cluster.shutdown();
328 return oss.str();
329 }
330
331 bufferlist inbl;
332 ret = cluster.mon_command(
333 "{\"prefix\": \"osd erasure-code-profile set\", \"name\": \"testprofile-" + pool_name + "\", \"profile\": [ \"k=2\", \"m=1\", \"ruleset-failure-domain=osd\"]}",
334 inbl, NULL, NULL);
335 if (ret) {
336 cluster.shutdown();
337 oss << "mon_command erasure-code-profile set name:testprofile-" << pool_name << " failed with error " << ret;
338 return oss.str();
339 }
340
341 ret = cluster.mon_command(
342 "{\"prefix\": \"osd pool create\", \"pool\": \"" + pool_name + "\", \"pool_type\":\"erasure\", \"pg_num\":8, \"pgp_num\":8, \"erasure_code_profile\":\"testprofile-" + pool_name + "\"}",
343 inbl, NULL, NULL);
344 if (ret) {
345 bufferlist inbl;
346 destroy_ec_profile_pp(cluster, pool_name, oss);
347 cluster.shutdown();
348 oss << "mon_command osd pool create pool:" << pool_name << " pool_type:erasure failed with error " << ret;
349 return oss.str();
350 }
351
352 cluster.wait_for_latest_osdmap();
353 return "";
354}
355
356std::string connect_cluster(rados_t *cluster)
357{
358 char *id = getenv("CEPH_CLIENT_ID");
359 if (id) std::cerr << "Client id is: " << id << std::endl;
360
361 int ret;
362 ret = rados_create(cluster, NULL);
363 if (ret) {
364 std::ostringstream oss;
365 oss << "rados_create failed with error " << ret;
366 return oss.str();
367 }
368 ret = rados_conf_read_file(*cluster, NULL);
369 if (ret) {
370 rados_shutdown(*cluster);
371 std::ostringstream oss;
372 oss << "rados_conf_read_file failed with error " << ret;
373 return oss.str();
374 }
375 rados_conf_parse_env(*cluster, NULL);
376 ret = rados_connect(*cluster);
377 if (ret) {
378 rados_shutdown(*cluster);
379 std::ostringstream oss;
380 oss << "rados_connect failed with error " << ret;
381 return oss.str();
382 }
383 return "";
384}
385
386std::string connect_cluster_pp(librados::Rados &cluster)
387{
388 return connect_cluster_pp(cluster, {});
389}
390
391std::string connect_cluster_pp(librados::Rados &cluster,
392 const std::map<std::string, std::string> &config)
393{
394 char *id = getenv("CEPH_CLIENT_ID");
395 if (id) std::cerr << "Client id is: " << id << std::endl;
396
397 int ret;
398 ret = cluster.init(id);
399 if (ret) {
400 std::ostringstream oss;
401 oss << "cluster.init failed with error " << ret;
402 return oss.str();
403 }
404 ret = cluster.conf_read_file(NULL);
405 if (ret) {
406 cluster.shutdown();
407 std::ostringstream oss;
408 oss << "cluster.conf_read_file failed with error " << ret;
409 return oss.str();
410 }
411 cluster.conf_parse_env(NULL);
412
413 for (auto &setting : config) {
414 ret = cluster.conf_set(setting.first.c_str(), setting.second.c_str());
415 if (ret) {
416 std::ostringstream oss;
417 oss << "failed to set config value " << setting.first << " to '"
418 << setting.second << "': " << cpp_strerror(ret);
419 return oss.str();
420 }
421 }
422
423 ret = cluster.connect();
424 if (ret) {
425 cluster.shutdown();
426 std::ostringstream oss;
427 oss << "cluster.connect failed with error " << ret;
428 return oss.str();
429 }
430 return "";
431}
432
433int destroy_one_pool(const std::string &pool_name, rados_t *cluster)
434{
435 int ret = rados_pool_delete(*cluster, pool_name.c_str());
436 if (ret) {
437 rados_shutdown(*cluster);
438 return ret;
439 }
440 rados_shutdown(*cluster);
441 return 0;
442}
443
444int destroy_one_ec_pool(const std::string &pool_name, rados_t *cluster)
445{
446 int ret = rados_pool_delete(*cluster, pool_name.c_str());
447 if (ret) {
448 rados_shutdown(*cluster);
449 return ret;
450 }
451
452 CephContext *cct = static_cast<CephContext*>(rados_cct(*cluster));
453 if (!cct->_conf->mon_fake_pool_delete) { // hope this is in [global]
454 std::ostringstream oss;
455 ret = destroy_ec_profile_and_ruleset(cluster, pool_name, oss);
456 if (ret) {
457 rados_shutdown(*cluster);
458 return ret;
459 }
460 }
461
462 rados_wait_for_latest_osdmap(*cluster);
463 rados_shutdown(*cluster);
464 return ret;
465}
466
467int destroy_one_pool_pp(const std::string &pool_name, Rados &cluster)
468{
469 int ret = cluster.pool_delete(pool_name.c_str());
470 if (ret) {
471 cluster.shutdown();
472 return ret;
473 }
474 cluster.shutdown();
475 return 0;
476}
477
478int destroy_one_ec_pool_pp(const std::string &pool_name, Rados &cluster)
479{
480 int ret = cluster.pool_delete(pool_name.c_str());
481 if (ret) {
482 cluster.shutdown();
483 return ret;
484 }
485
486 CephContext *cct = static_cast<CephContext*>(cluster.cct());
487 if (!cct->_conf->mon_fake_pool_delete) { // hope this is in [global]
488 std::ostringstream oss;
489 ret = destroy_ec_profile_and_ruleset_pp(cluster, pool_name, oss);
490 if (ret) {
491 cluster.shutdown();
492 return ret;
493 }
494 }
495
496 cluster.wait_for_latest_osdmap();
497 cluster.shutdown();
498 return ret;
499}
500
501void assert_eq_sparse(bufferlist& expected,
502 const std::map<uint64_t, uint64_t>& extents,
503 bufferlist& actual) {
504 auto i = expected.begin();
505 auto p = actual.begin();
506 uint64_t pos = 0;
507 for (auto extent : extents) {
508 const uint64_t start = extent.first;
509 const uint64_t end = start + extent.second;
510 for (; pos < end; ++i, ++pos) {
511 ASSERT_FALSE(i.end());
512 if (pos < start) {
513 // check the hole
514 ASSERT_EQ('\0', *i);
515 } else {
516 // then the extent
517 ASSERT_EQ(*i, *p);
518 ++p;
519 }
520 }
521 }
522 ASSERT_EQ(expected.length(), pos);
523}