]>
git.proxmox.com Git - ceph.git/blob - ceph/src/test/lazy-omap-stats/lazy_omap_stats_test.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2019 Red Hat
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.
17 #include <boost/algorithm/string/trim.hpp>
18 #include <boost/tokenizer.hpp>
19 #include <boost/uuid/uuid.hpp> // uuid class
20 #include <boost/uuid/uuid_generators.hpp> // generators
21 #include <boost/uuid/uuid_io.hpp> // streaming operators etc.
27 #include "common/ceph_json.h"
28 #include "global/global_init.h"
29 #include "include/compat.h"
31 #include "lazy_omap_stats_test.h"
35 void LazyOmapStatsTest::init(const int argc
, const char** argv
)
37 int ret
= rados
.init("admin");
40 cerr
<< "Failed to initialise rados! Error: " << ret
<< " " << strerror(ret
)
45 ret
= rados
.conf_parse_argv(argc
, argv
);
48 cerr
<< "Failed to parse command line config options! Error: " << ret
<< " "
49 << strerror(ret
) << endl
;
53 rados
.conf_parse_env(NULL
);
56 cerr
<< "Failed to parse environment! Error: " << ret
<< " "
57 << strerror(ret
) << endl
;
61 rados
.conf_read_file(NULL
);
64 cerr
<< "Failed to read config file! Error: " << ret
<< " " << strerror(ret
)
69 ret
= rados
.connect();
72 cerr
<< "Failed to connect to running cluster! Error: " << ret
<< " "
73 << strerror(ret
) << endl
;
79 "prefix
": "osd pool create
",
80 "pool
": ")" + conf.pool_name +
82 "pool_type
": "replicated
",
83 "size
": )" + to_string(conf
.replica_count
) +
86 librados::bufferlist inbl
;
88 ret
= rados
.mon_command(command
, inbl
, nullptr, &output
);
89 if (output
.length()) cout
<< output
<< endl
;
92 cerr
<< "Failed to create pool! Error: " << ret
<< " " << strerror(ret
)
97 ret
= rados
.ioctx_create(conf
.pool_name
.c_str(), io_ctx
);
100 cerr
<< "Failed to create ioctx! Error: " << ret
<< " " << strerror(ret
)
105 get_pool_id(conf
.pool_name
);
108 void LazyOmapStatsTest::shutdown()
110 rados
.pool_delete(conf
.pool_name
.c_str());
114 void LazyOmapStatsTest::write_omap(const string
& object_name
)
116 librados::bufferlist bl
;
117 int ret
= io_ctx
.write_full(object_name
, bl
);
120 cerr
<< "Failed to create object! Error: " << ret
<< " " << strerror(ret
)
124 ret
= io_ctx
.omap_set(object_name
, payload
);
127 cerr
<< "Failed to write omap payload! Error: " << ret
<< " "
128 << strerror(ret
) << endl
;
131 cout
<< "Wrote " << conf
.keys
<< " omap keys of " << conf
.payload_size
133 << "the " << object_name
<< " object" << endl
;
136 const string
LazyOmapStatsTest::get_name() const
138 boost::uuids::uuid uuid
= boost::uuids::random_generator()();
139 return boost::uuids::to_string(uuid
);
142 void LazyOmapStatsTest::write_many(uint how_many
)
144 for (uint i
= 0; i
< how_many
; i
++) {
145 write_omap(get_name());
149 void LazyOmapStatsTest::create_payload()
151 librados::bufferlist Lorem
;
153 "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do "
154 "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut "
155 "enim ad minim veniam, quis nostrud exercitation ullamco laboris "
156 "nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in "
157 "reprehenderit in voluptate velit esse cillum dolore eu fugiat "
158 "nulla pariatur. Excepteur sint occaecat cupidatat non proident, "
159 "sunt in culpa qui officia deserunt mollit anim id est laborum.");
160 conf
.payload_size
= Lorem
.length();
161 conf
.total_bytes
= conf
.keys
* conf
.payload_size
* conf
.how_many
;
162 conf
.total_keys
= conf
.keys
* conf
.how_many
;
164 for (i
= 1; i
< conf
.keys
+ 1; ++i
) {
165 payload
[get_name()] = Lorem
;
167 cout
<< "Created payload with " << conf
.keys
<< " keys of "
169 << " bytes each. Total size in bytes = " << conf
.keys
* conf
.payload_size
173 void LazyOmapStatsTest::scrub()
175 cout
<< "Scrubbing" << endl
;
177 cout
<< "Before scrub stamps:" << endl
;
178 string
target_pool(conf
.pool_id
);
179 target_pool
.append(".");
180 bool target_pool_found
= false;
181 map
<string
, string
> before_scrub
= get_scrub_stamps();
182 for (auto [pg
, stamp
] : before_scrub
) {
183 cout
<< "pg = " << pg
<< " stamp = " << stamp
<< endl
;
184 if (pg
.rfind(target_pool
, 0) == 0) {
185 target_pool_found
= true;
188 if (!target_pool_found
) {
189 cout
<< "Error: Target pool " << conf
.pool_name
<< ":" << conf
.pool_id
190 << " not found!" << endl
;
195 // Short sleep to make sure the new pool is visible
198 string command
= R
"({"prefix
": "osd deep
-scrub
", "who
": "all
"})";
199 auto output
= get_output(command
);
200 cout
<< output
<< endl
;
202 cout
<< "Waiting for deep-scrub to complete..." << endl
;
203 while (sleep(1) == 0) {
204 cout
<< "Current scrub stamps:" << endl
;
205 bool complete
= true;
206 map
<string
, string
> current_stamps
= get_scrub_stamps();
207 for (auto [pg
, stamp
] : current_stamps
) {
208 cout
<< "pg = " << pg
<< " stamp = " << stamp
<< endl
;
209 if (stamp
== before_scrub
[pg
]) {
210 // See if stamp for each pg has changed
211 // If not, we haven't completed the deep-scrub
220 cout
<< "Scrubbing complete" << endl
;
223 const int LazyOmapStatsTest::find_matches(string
& output
, regex
& reg
) const
225 sregex_iterator
cur(output
.begin(), output
.end(), reg
);
227 for (auto end
= std::sregex_iterator(); cur
!= end
; ++cur
) {
228 cout
<< (*cur
)[1].str() << endl
;
234 const string
LazyOmapStatsTest::get_output(const string command
,
236 const CommandTarget target
)
238 librados::bufferlist inbl
, outbl
;
241 if (target
== CommandTarget::TARGET_MON
) {
242 ret
= rados
.mon_command(command
, inbl
, &outbl
, &output
);
244 ret
= rados
.mgr_command(command
, inbl
, &outbl
, &output
);
246 if (output
.length() && !silent
) {
247 cout
<< output
<< endl
;
251 cerr
<< "Failed to get " << command
<< "! Error: " << ret
<< " "
252 << strerror(ret
) << endl
;
255 return string(outbl
.c_str(), outbl
.length());
258 void LazyOmapStatsTest::get_pool_id(const string
& pool
)
260 cout
<< R
"(Querying pool id)" << endl
;
262 string command
= R
"({"prefix
": "osd pool ls
", "detail
": "detail
", "format
": "json
"})";
263 librados::bufferlist inbl
, outbl
;
264 auto output
= get_output(command
, false, CommandTarget::TARGET_MON
);
266 parser
.parse(output
.c_str(), output
.size());
267 for (const auto& pool
: parser
.get_array_elements()) {
269 parser2
.parse(pool
.c_str(), static_cast<int>(pool
.size()));
270 auto* obj
= parser2
.find_obj("pool_name");
271 if (obj
->get_data().compare(conf
.pool_name
) == 0) {
272 obj
= parser2
.find_obj("pool_id");
273 conf
.pool_id
= obj
->get_data();
276 if (conf
.pool_id
.empty()) {
277 cout
<< "Failed to find pool ID for pool " << conf
.pool_name
<< "!" << endl
;
280 cout
<< "Found pool ID: " << conf
.pool_id
<< endl
;
284 map
<string
, string
> LazyOmapStatsTest::get_scrub_stamps() {
285 map
<string
, string
> stamps
;
286 string command
= R
"({"prefix
": "pg dump
", "format
": "json
"})";
287 auto output
= get_output(command
);
289 parser
.parse(output
.c_str(), output
.size());
290 auto* obj
= parser
.find_obj("pg_map")->find_obj("pg_stats");
291 for (auto pg
= obj
->find_first(); !pg
.end(); ++pg
) {
292 stamps
.insert({(*pg
)->find_obj("pgid")->get_data(),
293 (*pg
)->find_obj("last_deep_scrub_stamp")->get_data()});
298 void LazyOmapStatsTest::check_one()
300 string full_output
= get_output();
301 cout
<< full_output
<< endl
;
305 "\n)OSD_STAT"); // Strip OSD_STAT table so we don't find matches there
307 regex_search(full_output
, match
, reg
);
308 auto truncated_output
= match
[1].str();
309 cout
<< truncated_output
<< endl
;
313 to_string(conf
.keys
) +
317 cout
<< "Checking number of keys " << conf
.keys
<< endl
;
318 cout
<< "Found the following lines" << endl
;
319 cout
<< "*************************" << endl
;
320 uint result
= find_matches(truncated_output
, reg
);
321 cout
<< "**********************" << endl
;
322 cout
<< "Found " << result
<< " matching line(s)" << endl
;
328 to_string(conf
.payload_size
* conf
.keys
) +
331 cout
<< "Checking number of bytes "
332 << conf
.payload_size
* conf
.keys
<< endl
;
333 cout
<< "Found the following lines" << endl
;
334 cout
<< "*************************" << endl
;
335 result
= find_matches(truncated_output
, reg
);
336 cout
<< "**********************" << endl
;
337 cout
<< "Found " << result
<< " matching line(s)" << endl
;
341 cout
<< "Error: Found " << total
<< " matches, expected 6! Exiting..."
345 cout
<< "check_one successful. Found " << total
<< " matches as expected"
349 const int LazyOmapStatsTest::find_index(string
& haystack
, regex
& needle
,
353 regex_search(haystack
, match
, needle
);
354 auto line
= match
[1].str();
355 boost::algorithm::trim(line
);
356 boost::char_separator
<char> sep
{" "};
357 boost::tokenizer
<boost::char_separator
<char>> tok(line
, sep
);
358 vector
<string
> tokens(tok
.begin(), tok
.end());
359 auto it
= find(tokens
.begin(), tokens
.end(), label
);
360 if (it
!= tokens
.end()) {
361 return distance(tokens
.begin(), it
);
364 cerr
<< "find_index failed to find index for " << label
<< endl
;
366 return -1; // Unreachable
369 const uint
LazyOmapStatsTest::tally_column(const uint omap_bytes_index
,
373 istringstream
buffer(table
);
376 while (std::getline(buffer
, line
)) {
381 boost::char_separator
<char> sep
{" "};
382 boost::tokenizer
<boost::char_separator
<char>> tok(line
, sep
);
383 vector
<string
> tokens(tok
.begin(), tok
.end());
384 total
+= stoi(tokens
.at(omap_bytes_index
));
390 void LazyOmapStatsTest::check_column(const int index
, const string
& table
,
391 const string
& type
, bool header
) const
395 if (type
.compare("bytes") == 0) {
396 expected
= conf
.total_bytes
;
397 errormsg
= "Error. Got unexpected byte count!";
399 expected
= conf
.total_keys
;
400 errormsg
= "Error. Got unexpected key count!";
402 uint sum
= tally_column(index
, table
, header
);
403 cout
<< "Got: " << sum
<< " Expected: " << expected
<< endl
;
404 if (sum
!= expected
) {
405 cout
<< errormsg
<< endl
;
410 index_t
LazyOmapStatsTest::get_indexes(regex
& reg
, string
& output
) const
413 indexes
.byte_index
= find_index(output
, reg
, "OMAP_BYTES*");
414 indexes
.key_index
= find_index(output
, reg
, "OMAP_KEYS*");
419 void LazyOmapStatsTest::check_pg_dump()
421 cout
<< R
"(Checking "pg dump
" output)" << endl
;
423 string dump_output
= get_output();
424 cout
<< dump_output
<< endl
;
430 index_t indexes
= get_indexes(reg
, dump_output
);
434 R
"((PG_STAT[\s\S]*))"
437 regex_search(dump_output
, match
, reg
);
438 auto table
= match
[1].str();
440 cout
<< "Checking bytes" << endl
;
441 check_column(indexes
.byte_index
, table
, string("bytes"));
443 cout
<< "Checking keys" << endl
;
444 check_column(indexes
.key_index
, table
, string("keys"));
449 void LazyOmapStatsTest::check_pg_dump_summary()
451 cout
<< R
"(Checking "pg dump summary
" output)" << endl
;
453 string command
= R
"({"prefix
": "pg dump
", "dumpcontents
": ["summary
"]})";
454 string dump_output
= get_output(command
);
455 cout
<< dump_output
<< endl
;
461 index_t indexes
= get_indexes(reg
, dump_output
);
468 regex_search(dump_output
, match
, reg
);
469 auto table
= match
[1].str();
471 cout
<< "Checking bytes" << endl
;
472 check_column(indexes
.byte_index
, table
, string("bytes"), false);
474 cout
<< "Checking keys" << endl
;
475 check_column(indexes
.key_index
, table
, string("keys"), false);
479 void LazyOmapStatsTest::check_pg_dump_pgs()
481 cout
<< R
"(Checking "pg dump pgs
" output)" << endl
;
483 string command
= R
"({"prefix
": "pg dump
", "dumpcontents
": ["pgs
"]})";
484 string dump_output
= get_output(command
);
485 cout
<< dump_output
<< endl
;
487 regex
reg(R
"(^(PG_STAT\s.*))"
489 index_t indexes
= get_indexes(reg
, dump_output
);
491 reg
= R
"(^(PG_STAT[\s\S]*))"
494 regex_search(dump_output
, match
, reg
);
495 auto table
= match
[1].str();
497 cout
<< "Checking bytes" << endl
;
498 check_column(indexes
.byte_index
, table
, string("bytes"));
500 cout
<< "Checking keys" << endl
;
501 check_column(indexes
.key_index
, table
, string("keys"));
505 void LazyOmapStatsTest::check_pg_dump_pools()
507 cout
<< R
"(Checking "pg dump pools
" output)" << endl
;
509 string command
= R
"({"prefix
": "pg dump
", "dumpcontents
": ["pools
"]})";
510 string dump_output
= get_output(command
);
511 cout
<< dump_output
<< endl
;
513 regex
reg(R
"(^(POOLID\s.*))"
515 index_t indexes
= get_indexes(reg
, dump_output
);
524 regex_search(dump_output
, match
, reg
);
525 auto line
= match
[1].str();
527 cout
<< "Checking bytes" << endl
;
528 check_column(indexes
.byte_index
, line
, string("bytes"), false);
530 cout
<< "Checking keys" << endl
;
531 check_column(indexes
.key_index
, line
, string("keys"), false);
535 void LazyOmapStatsTest::check_pg_ls()
537 cout
<< R
"(Checking "pg ls
" output)" << endl
;
539 string command
= R
"({"prefix
": "pg ls
"})";
540 string dump_output
= get_output(command
);
541 cout
<< dump_output
<< endl
;
543 regex
reg(R
"(^(PG\s.*))"
545 index_t indexes
= get_indexes(reg
, dump_output
);
547 reg
= R
"(^(PG[\s\S]*))"
550 regex_search(dump_output
, match
, reg
);
551 auto table
= match
[1].str();
553 cout
<< "Checking bytes" << endl
;
554 check_column(indexes
.byte_index
, table
, string("bytes"));
556 cout
<< "Checking keys" << endl
;
557 check_column(indexes
.key_index
, table
, string("keys"));
561 void LazyOmapStatsTest::wait_for_active_clean()
563 cout
<< "Waiting for active+clean" << endl
;
568 R
"((PG_STAT[\s\S]*))"
570 string command
= R
"({"prefix
": "pg dump
"})";
573 string dump_output
= get_output(command
, true);
579 index
= find_index(dump_output
, ireg
, "STATE");
582 regex_search(dump_output
, match
, reg
);
583 istringstream
buffer(match
[1].str());
586 while (std::getline(buffer
, line
)) {
587 if (line
.compare(0, 1, "P") == 0) continue;
588 boost::char_separator
<char> sep
{" "};
589 boost::tokenizer
<boost::char_separator
<char>> tok(line
, sep
);
590 vector
<string
> tokens(tok
.begin(), tok
.end());
591 num_not_clean
+= tokens
.at(index
).compare("active+clean");
593 cout
<< "." << flush
;
594 this_thread::sleep_for(chrono::milliseconds(250));
595 } while (num_not_clean
);
600 const int LazyOmapStatsTest::run(const int argc
, const char** argv
)
604 wait_for_active_clean();
605 write_omap(get_name());
609 write_many(conf
.how_many
- 1); // Since we already wrote one
612 check_pg_dump_summary();
614 check_pg_dump_pools();
616 cout
<< "All tests passed. Success!" << endl
;