// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab #include "common/Formatter.h" #include "include/stringify.h" #include "json_spirit/json_spirit.h" #include "test_common.h" using namespace std; namespace { using namespace ceph; int wait_for_healthy(rados_t *cluster) { bool healthy = false; // This timeout is very long because the tests are sometimes // run on a thrashing cluster int timeout = 3600; int slept = 0; while(!healthy) { JSONFormatter cmd_f; cmd_f.open_object_section("command"); cmd_f.dump_string("prefix", "status"); cmd_f.dump_string("format", "json"); cmd_f.close_section(); std::ostringstream cmd_stream; cmd_f.flush(cmd_stream); const std::string serialized_cmd = cmd_stream.str(); const char *cmd[2]; cmd[1] = NULL; cmd[0] = serialized_cmd.c_str(); char *outbuf = NULL; size_t outlen = 0; int ret = rados_mon_command(*cluster, (const char **)cmd, 1, "", 0, &outbuf, &outlen, NULL, NULL); if (ret) { return ret; } std::string out(outbuf, outlen); rados_buffer_free(outbuf); json_spirit::mValue root; [[maybe_unused]] bool json_parse_success = json_spirit::read(out, root); ceph_assert(json_parse_success); json_spirit::mObject root_obj = root.get_obj(); json_spirit::mObject pgmap = root_obj["pgmap"].get_obj(); json_spirit::mArray pgs_by_state = pgmap["pgs_by_state"].get_array(); if (pgs_by_state.size() == 1) { json_spirit::mObject state = pgs_by_state[0].get_obj(); std::string state_name = state["state_name"].get_str(); if (state_name != std::string("active+clean")) { healthy = false; } else { healthy = true; } } else { healthy = false; } if (slept >= timeout) { return -ETIMEDOUT; }; if (!healthy) { sleep(1); slept += 1; } } return 0; } int rados_pool_set( rados_t *cluster, const std::string &pool_name, const std::string &var, const std::string &val) { JSONFormatter cmd_f; cmd_f.open_object_section("command"); cmd_f.dump_string("prefix", "osd pool set"); cmd_f.dump_string("pool", pool_name); cmd_f.dump_string("var", var); cmd_f.dump_string("val", val); cmd_f.close_section(); std::ostringstream cmd_stream; cmd_f.flush(cmd_stream); const std::string serialized_cmd = cmd_stream.str(); const char *cmd[2]; cmd[1] = NULL; cmd[0] = serialized_cmd.c_str(); int ret = rados_mon_command(*cluster, (const char **)cmd, 1, "", 0, NULL, NULL, NULL, NULL); return ret; } struct pool_op_error : std::exception { string msg; pool_op_error(const std::string& pool_name, const std::string& func_name, int err) { std::ostringstream oss; oss << func_name << "(" << pool_name << ") failed with error " << err; msg = oss.str(); } const char* what() const noexcept override { return msg.c_str(); } }; template std::string with_healthy_cluster(rados_t* cluster, const std::string& pool_name, Func&& func) { try { // Wait for 'creating/backfilling' to clear if (int r = wait_for_healthy(cluster); r != 0) { throw pool_op_error{pool_name, "wait_for_healthy", r}; } func(); // Wait for 'creating/backfilling' to clear if (int r = wait_for_healthy(cluster); r != 0) { throw pool_op_error{pool_name, "wait_for_healthy", r}; } } catch (const pool_op_error& e) { return e.what(); } return ""; } } std::string set_pg_num( rados_t *cluster, const std::string &pool_name, uint32_t pg_num) { return with_healthy_cluster(cluster, pool_name, [&] { // Adjust pg_num if (int r = rados_pool_set(cluster, pool_name, "pg_num", stringify(pg_num)); r != 0) { throw pool_op_error{pool_name, "set_pg_num", r}; } }); } std::string set_pgp_num( rados_t *cluster, const std::string &pool_name, uint32_t pgp_num) { return with_healthy_cluster(cluster, pool_name, [&] { // Adjust pgp_num if (int r = rados_pool_set(cluster, pool_name, "pgp_num", stringify(pgp_num)); r != 0) { throw pool_op_error{pool_name, "set_pgp_num", r}; } }); }