Split the program option readers out into a seperate class from the main logic controlling the operationg of ceph_test_rados_io_sequence
Signed-off-by: Jon Bailey <jonathan.bailey1@ibm.com>
--- /dev/null
+#pragma once
+
+#include <optional>
+#include <string>
+
+#include <boost/program_options.hpp>
+
+#include "include/ceph_assert.h"
+#include "include/random.h"
+
+/* Overview
+ *
+ * class ProgramOptionReader
+ * Base class that is constructed using a variable_map from
+ * boost::program_options and the name of an option.
+ * This class extracts the named option from the variable map into the
+ * force_value variable.
+ * It is necissairy to override this class and implement the pure virtual
+ * select() function to convert the input type to the return type as well as
+ * implement how to calculate a default value if no input is given.
+ *
+ * ProgramOptionSelector
+ * Implementation of the above ProgramOptionReader.
+ * This is constructed with a list of options and implements the select
+ * function to return randomly from this list when called and no argument is
+ * given in the variables_map.
+ * The first time this is called, we will always return the first item of the
+ * list when select_first is true and no value is found in the variables_map.
+ * This takes an random_number_generator so that randomness can be controlled
+ * and reproduced if desired.
+ *
+ */
+
+namespace po = boost::program_options;
+
+namespace ceph {
+namespace io_sequence {
+namespace tester {
+template <typename option_type,
+ typename return_type = option_type>
+class ProgramOptionReader {
+ public:
+ ProgramOptionReader(po::variables_map& vm,
+ const std::string& option_name)
+ : option_name(option_name) {
+ if (vm.count(option_name) > 0) {
+ force_value = vm[option_name].as<option_type>();
+ }
+ }
+
+ virtual ~ProgramOptionReader() = default;
+
+ bool isForced() { return force_value.has_value(); }
+
+ virtual const return_type select() = 0;
+
+ protected:
+ std::optional<option_type> force_value;
+
+ std::string option_name;
+};
+
+template <typename option_type,
+ int num_selections,
+ const std::array< option_type,
+ num_selections>& selections_array>
+class ProgramOptionSelector : public ProgramOptionReader<option_type> {
+ public:
+ ProgramOptionSelector(ceph::util::random_number_generator<int>& rng,
+ po::variables_map& vm,
+ const std::string& option_name,
+ bool select_first)
+ : ProgramOptionReader<option_type>(vm, option_name), rng(rng) {
+ if (select_first) {
+ ceph_assert(selections_array.size() > 0);
+ first_value = selections_array[0];
+ }
+ }
+
+ virtual ~ProgramOptionSelector() = default;
+
+ virtual const option_type select() override {
+ if (this->force_value.has_value()) {
+ return *this->force_value;
+ } else if (first_value.has_value()) {
+ return *std::exchange(first_value, std::nullopt);
+ } else {
+ return selections_array[rng(num_selections - 1)];
+ }
+ }
+
+ protected:
+ ceph::util::random_number_generator<int>& rng;
+
+ std::optional<option_type> first_value;
+};
+} // namespace io_sequence
+} // namespace tester
+} // namespace ceph
\ No newline at end of file
template <typename S>
int send_mon_command(S& s, librados::Rados& rados, const char* name,
- ceph::buffer::list& inbl, ceph::buffer::list* outbl, Formatter* f) {
+ ceph::buffer::list& inbl, ceph::buffer::list* outbl,
+ Formatter* f) {
std::ostringstream oss;
encode_json(name, s, f);
f->flush(oss);
} // namespace
-template <typename T, int N, const std::array<T, N>& Ts>
-ceph::io_sequence::tester::ProgramOptionSelector<T, N, Ts>::
- ProgramOptionSelector(ceph::util::random_number_generator<int>& rng,
- po::variables_map vm, const std::string& option_name,
- bool set_forced, bool select_first)
- : rng(rng), option_name(option_name) {
- if (set_forced && vm.count(option_name)) {
- force_value = vm[option_name].as<T>();
- }
- if (select_first) {
- ceph_assert(choices.size() > 0);
- first_value = choices[0];
- }
-}
-
-template <typename T, int N, const std::array<T, N>& Ts>
-bool ceph::io_sequence::tester::ProgramOptionSelector<T, N, Ts>::isForced() {
- return force_value.has_value();
-}
-
-template <typename T, int N, const std::array<T, N>& Ts>
-const T ceph::io_sequence::tester::ProgramOptionSelector<T, N, Ts>::choose() {
- if (force_value.has_value()) {
- return *force_value;
- } else if (first_value.has_value()) {
- return *std::exchange(first_value, std::nullopt);
- } else {
- return choices[rng(N - 1)];
- }
-}
-
-ceph::io_sequence::tester::SelectObjectSize::SelectObjectSize(
- ceph::util::random_number_generator<int>& rng, po::variables_map vm)
- : ProgramOptionSelector(rng, vm, "objectsize", true, true) {}
-
-ceph::io_sequence::tester::SelectBlockSize::SelectBlockSize(
- ceph::util::random_number_generator<int>& rng, po::variables_map vm)
- : ProgramOptionSelector(rng, vm, "blocksize", true, true) {}
-
-ceph::io_sequence::tester::SelectNumThreads::SelectNumThreads(
- ceph::util::random_number_generator<int>& rng, po::variables_map vm)
- : ProgramOptionSelector(rng, vm, "threads", true, true) {}
-
-ceph::io_sequence::tester::SelectSeqRange::SelectSeqRange(
- ceph::util::random_number_generator<int>& rng, po::variables_map vm)
- : ProgramOptionSelector(rng, vm, "sequence", false, false) {
+ceph::io_sequence::tester::SelectSeqRange::SelectSeqRange(po::variables_map& vm)
+ : ProgramOptionReader<std::pair<ceph::io_exerciser::Sequence,
+ ceph::io_exerciser::Sequence>>(vm,
+ "sequence") {
if (vm.count(option_name)) {
ceph::io_exerciser::Sequence s =
static_cast<ceph::io_exerciser::Sequence>(vm["sequence"].as<int>());
}
const std::pair<ceph::io_exerciser::Sequence, ceph::io_exerciser::Sequence>
-ceph::io_sequence::tester::SelectSeqRange::choose() {
+ceph::io_sequence::tester::SelectSeqRange::select() {
if (force_value.has_value()) {
return *force_value;
} else {
}
}
-ceph::io_sequence::tester::SelectErasureKM::SelectErasureKM(
- ceph::util::random_number_generator<int>& rng, po::variables_map vm)
- : ProgramOptionSelector(rng, vm, "km", true, true) {}
-
-ceph::io_sequence::tester::SelectErasurePlugin::SelectErasurePlugin(
- ceph::util::random_number_generator<int>& rng, po::variables_map vm)
- : ProgramOptionSelector(rng, vm, "plugin", true, false) {}
-
-ceph::io_sequence::tester::SelectErasureChunkSize::SelectErasureChunkSize(
- ceph::util::random_number_generator<int>& rng, po::variables_map vm)
- : ProgramOptionSelector(rng, vm, "chunksize", true, true) {}
-
-ceph::io_sequence::tester::SelectECPool::SelectECPool(
+ceph::io_sequence::tester::SelectErasurePool::SelectErasurePool(
ceph::util::random_number_generator<int>& rng, po::variables_map vm,
librados::Rados& rados, bool dry_run, bool allow_pool_autoscaling,
bool allow_pool_balancer, bool allow_pool_deep_scrubbing,
bool allow_pool_scrubbing, bool test_recovery)
- : ProgramOptionSelector(rng, vm, "pool", false, false),
+ : ProgramOptionReader<std::string>(vm, "pool"),
rados(rados),
dry_run(dry_run),
allow_pool_autoscaling(allow_pool_autoscaling),
allow_pool_deep_scrubbing(allow_pool_deep_scrubbing),
allow_pool_scrubbing(allow_pool_scrubbing),
test_recovery(test_recovery),
- skm(SelectErasureKM(rng, vm)),
- spl(SelectErasurePlugin(rng, vm)),
- scs(SelectErasureChunkSize(rng, vm)) {
+ skm{rng, vm, "km", true},
+ spl{rng, vm, "plugin", true},
+ scs{rng, vm, "chunksize", true} {
if (!skm.isForced()) {
if (vm.count("pool")) {
force_value = vm["pool"].as<std::string>();
}
}
-const std::string ceph::io_sequence::tester::SelectECPool::choose() {
+const std::string ceph::io_sequence::tester::SelectErasurePool::select() {
std::pair<int, int> value;
if (!skm.isForced() && force_value.has_value()) {
int rc;
m = reply.m;
return *force_value;
} else {
- value = skm.choose();
+ value = skm.select();
}
k = value.first;
m = value.second;
- const std::string plugin = std::string(spl.choose());
- const uint64_t chunk_size = scs.choose();
+ const std::string plugin = std::string(spl.select());
+ const uint64_t chunk_size = scs.select();
std::string pool_name = "ec_" + plugin + "_cs" + std::to_string(chunk_size) +
"_k" + std::to_string(k) + "_m" + std::to_string(m);
return pool_name;
}
-void ceph::io_sequence::tester::SelectECPool::create_pool(
+void ceph::io_sequence::tester::SelectErasurePool::create_pool(
librados::Rados& rados, const std::string& pool_name,
const std::string& plugin, uint64_t chunk_size, int k, int m) {
int rc;
ceph::messaging::config::ConfigSetRequest configSetBluestoreDebugRequest{
"global", "bluestore_debug_inject_read_err", "true", std::nullopt};
rc = send_mon_command(configSetBluestoreDebugRequest, rados,
- "ConfigSetRequest", inbl, &outbl,
- formatter.get());
+ "ConfigSetRequest", inbl, &outbl, formatter.get());
ceph_assert(rc == 0);
ceph::messaging::config::ConfigSetRequest configSetMaxMarkdownRequest{
"global", "osd_max_markdown_count", "99999999", std::nullopt};
- rc =
- send_mon_command(configSetMaxMarkdownRequest, rados, "ConfigSetRequest",
- inbl, &outbl, formatter.get());
+ rc = send_mon_command(configSetMaxMarkdownRequest, rados,
+ "ConfigSetRequest", inbl, &outbl, formatter.get());
ceph_assert(rc == 0);
}
}
ceph::io_sequence::tester::TestObject::TestObject(
const std::string oid, librados::Rados& rados,
- boost::asio::io_context& asio, SelectBlockSize& sbs, SelectECPool& spo,
+ boost::asio::io_context& asio, SelectBlockSize& sbs, SelectErasurePool& spo,
SelectObjectSize& sos, SelectNumThreads& snt, SelectSeqRange& ssr,
ceph::util::random_number_generator<int>& rng, ceph::mutex& lock,
ceph::condition_variable& cond, bool dryrun, bool verbose,
: rng(rng), verbose(verbose), seqseed(seqseed), testrecovery(testrecovery) {
if (dryrun) {
exerciser_model = std::make_unique<ceph::io_exerciser::ObjectModel>(
- oid, sbs.choose(), rng());
+ oid, sbs.select(), rng());
} else {
- const std::string pool = spo.choose();
+ const std::string pool = spo.select();
poolK = spo.getChosenK();
poolM = spo.getChosenM();
- int threads = snt.choose();
+ int threads = snt.select();
bufferlist inbl, outbl;
auto formatter = std::make_unique<JSONFormatter>(false);
}
exerciser_model = std::make_unique<ceph::io_exerciser::RadosIo>(
- rados, asio, pool, oid, cached_shard_order, sbs.choose(), rng(),
+ rados, asio, pool, oid, cached_shard_order, sbs.select(), rng(),
threads, lock, cond);
dout(0) << "= " << oid << " pool=" << pool << " threads=" << threads
<< " blocksize=" << exerciser_model->get_block_size() << " ="
<< dendl;
}
- obj_size_range = sos.choose();
- seq_range = ssr.choose();
+ obj_size_range = sos.select();
+ seq_range = ssr.select();
curseq = seq_range.first;
if (testrecovery) {
: rados(rados),
seed(vm.contains("seed") ? vm["seed"].as<int>() : time(nullptr)),
rng(ceph::util::random_number_generator<int>(seed)),
- sbs{rng, vm},
- sos{rng, vm},
+ sbs{rng, vm, "blocksize", true},
+ sos{rng, vm, "objectsize", true},
spo{rng,
vm,
rados,
vm.contains("allow_pool_deep_scrubbing"),
vm.contains("allow_pool_scrubbing"),
vm.contains("test_recovery")},
- snt{rng, vm},
- ssr{rng, vm} {
+ snt{rng, vm, "threads", true},
+ ssr{vm} {
dout(0) << "Test using seed " << seed << dendl;
verbose = vm.contains("verbose");
void ceph::io_sequence::tester::TestRunner::list_sequence(bool testrecovery) {
// List seqeunces
- std::pair<int, int> obj_size_range = sos.choose();
+ std::pair<int, int> obj_size_range = sos.select();
ceph::io_exerciser::Sequence s = ceph::io_exerciser::Sequence::SEQUENCE_BEGIN;
std::unique_ptr<ceph::io_exerciser::IoSequence> seq;
if (testrecovery) {
if (dryrun) {
model = std::make_unique<ceph::io_exerciser::ObjectModel>(
- object_name, sbs.choose(), rng());
+ object_name, sbs.select(), rng());
} else {
- const std::string pool = spo.choose();
+ const std::string pool = spo.select();
bufferlist inbl, outbl;
auto formatter = std::make_unique<JSONFormatter>(false);
reply.decode_json(&p);
model = std::make_unique<ceph::io_exerciser::RadosIo>(
- rados, asio, pool, object_name, reply.acting, sbs.choose(), rng(),
+ rados, asio, pool, object_name, reply.acting, sbs.select(), rng(),
1, // 1 thread
lock, cond);
}
#include <optional>
#include <utility>
+#include "ProgramOptionReader.h"
#include "common/io_exerciser/IoOp.h"
#include "common/io_exerciser/IoSequence.h"
#include "common/io_exerciser/Model.h"
#include <optional>
/* Overview
- *
- * class ProgramOptionSelector
- * Base class for selector objects below with common code for
- * selecting options
*
* class SelectObjectSize
* Selects min and max object sizes for a test
*
+ * class SelectBlockSize
+ * Selects a block size for a test
+ *
+ * class SelectNumThreads
+ * Selects number of threads for a test
+ *
* class SelectErasureKM
* Selects an EC k and m value for a test
*
+ * class SelectErasureChunkSize
+ * Selects a chunk size/stripe unit for the test
+ *
* class SelectErasurePlugin
* Selects an plugin for a test
*
- * class SelectECPool
+ * class SelectErasurePool
* Selects an EC pool (plugin,k and m) for a test. Also creates the
* pool as well.
*
- * class SelectBlockSize
- * Selects a block size for a test
- *
- * class SelectNumThreads
- * Selects number of threads for a test
- *
* class SelectSeqRange
* Selects a sequence or range of sequences for a test
*
+ * class SelectErasurePool
+ * Selects a pool name for a test
+ *
* class TestObject
* Runs a test against an object, generating IOSequence
* and applying them to an IoExerciser
*
+ * class TestRunner
+ * Determines test type to run, creates and orchestrates automated and
+ * interactive tests as well as creating a test object for each automated test
+ * we want to run in parallel
+ *
* main
* Run sequences of I/O with data integrity checking to
* one or more objects in parallel. Without arguments
namespace po = boost::program_options;
namespace ceph {
-namespace io_sequence::tester {
+namespace io_sequence {
+namespace tester {
// Choices for min and max object size
-inline constexpr size_t objectSizeSize = 10;
-inline constexpr std::array<std::pair<int, int>, objectSizeSize>
+// Choices for min and max object size
+inline static constexpr size_t objectSizeSize = 10;
+inline static constexpr std::array<std::pair<int, int>, objectSizeSize>
objectSizeChoices = {{{1, 32}, // Default - best for boundary checking
{12, 14},
{28, 30},
{83, 83},
{97, 97}}};
+using SelectObjectSize =
+ ProgramOptionSelector<std::pair<int, int>,
+ io_sequence::tester ::objectSizeSize,
+ io_sequence::tester ::objectSizeChoices>;
+
// Choices for block size
-inline constexpr int blockSizeSize = 5;
-inline constexpr std::array<uint64_t, blockSizeSize> blockSizeChoices = {
+inline static constexpr int blockSizeSize = 5;
+inline static constexpr std::array<uint64_t, blockSizeSize> blockSizeChoices = {
{2048, // Default - test boundaries for EC 4K chunk size
512, 3767, 4096, 32768}};
+using SelectBlockSize =
+ ProgramOptionSelector<uint64_t,
+ io_sequence::tester ::blockSizeSize,
+ io_sequence::tester ::blockSizeChoices>;
+
// Choices for number of threads
-inline constexpr int threadArraySize = 4;
-inline constexpr std::array<int, threadArraySize> threadCountChoices = {
+inline static constexpr int threadArraySize = 4;
+inline static constexpr std::array<int, threadArraySize> threadCountChoices = {
{1, // Default
2, 4, 8}};
+using SelectNumThreads =
+ ProgramOptionSelector<int,
+ io_sequence::tester ::threadArraySize,
+ io_sequence::tester ::threadCountChoices>;
+
// Choices for EC k+m profile
inline constexpr int kmSize = 6;
inline constexpr std::array<std::pair<int, int>, kmSize> kmChoices = {
{4, 2},
{5, 1}}};
+using SelectErasureKM =
+ ProgramOptionSelector<std::pair<int, int>,
+ io_sequence::tester ::kmSize,
+ io_sequence::tester::kmChoices>;
+
// Choices for EC chunk size
-inline constexpr int chunkSizeSize = 3;
-inline constexpr std::array<uint64_t, chunkSizeSize> chunkSizeChoices = {
+inline static constexpr int chunkSizeSize = 3;
+inline static constexpr std::array<uint64_t, chunkSizeSize> chunkSizeChoices = {
{4 * 1024, 64 * 1024, 256 * 1024}};
-// Choices for plugin
-inline constexpr int pluginListSize = 2;
-inline constexpr std::array<std::string_view, pluginListSize> pluginChoices = {
- {"jerasure", "isa"}};
-
-inline constexpr std::array<
- std::pair<ceph::io_exerciser::Sequence, ceph::io_exerciser::Sequence>, 0>
- sequencePairs = {{}};
-
-inline constexpr std::array<std::string, 0> poolChoices = {{}};
-
-template <typename T, int N, const std::array<T, N>& Ts>
-class ProgramOptionSelector {
- public:
- ProgramOptionSelector(ceph::util::random_number_generator<int>& rng,
- po::variables_map vm, const std::string& option_name,
- bool set_forced, bool select_first);
- virtual ~ProgramOptionSelector() = default;
- bool isForced();
- virtual const T choose();
-
- protected:
- ceph::util::random_number_generator<int>& rng;
- static constexpr std::array<T, N> choices = Ts;
-
- std::optional<T> force_value;
- std::optional<T> first_value;
-
- std::string option_name;
-};
+using SelectErasureChunkSize =
+ ProgramOptionSelector<uint64_t,
+ io_sequence::tester ::chunkSizeSize,
+ io_sequence::tester::chunkSizeChoices>;
-class SelectObjectSize
- : public ProgramOptionSelector<std::pair<int, int>,
- io_sequence::tester::objectSizeSize,
- io_sequence::tester::objectSizeChoices> {
- public:
- SelectObjectSize(ceph::util::random_number_generator<int>& rng,
- po::variables_map vm);
-};
+// Choices for plugin
+inline static constexpr int pluginListSize = 2;
+inline static constexpr std::array<std::string_view, pluginListSize>
+ pluginChoices = {{"jerasure", "isa"}};
-class SelectBlockSize
- : public ProgramOptionSelector<uint64_t, io_sequence::tester::blockSizeSize,
- io_sequence::tester::blockSizeChoices> {
- public:
- SelectBlockSize(ceph::util::random_number_generator<int>& rng,
- po::variables_map vm);
-};
-
-class SelectNumThreads
- : public ProgramOptionSelector<int, io_sequence::tester::threadArraySize,
- io_sequence::tester::threadCountChoices> {
- public:
- SelectNumThreads(ceph::util::random_number_generator<int>& rng,
- po::variables_map vm);
-};
+using SelectErasurePlugin =
+ ProgramOptionSelector<std::string_view,
+ io_sequence::tester ::pluginListSize,
+ io_sequence::tester ::pluginChoices>;
class SelectSeqRange
- : public ProgramOptionSelector<
- std::pair<ceph::io_exerciser::Sequence, ceph::io_exerciser::Sequence>,
- 0, io_sequence::tester::sequencePairs> {
+ : public ProgramOptionReader<std::pair<ceph::io_exerciser ::Sequence,
+ ceph::io_exerciser ::Sequence>> {
public:
- SelectSeqRange(ceph::util::random_number_generator<int>& rng,
- po::variables_map vm);
-
+ SelectSeqRange(po::variables_map& vm);
const std::pair<ceph::io_exerciser::Sequence, ceph::io_exerciser::Sequence>
- choose() override;
-};
-
-class SelectErasureKM
- : public ProgramOptionSelector<std::pair<int, int>,
- io_sequence::tester::kmSize,
- io_sequence::tester::kmChoices> {
- public:
- SelectErasureKM(ceph::util::random_number_generator<int>& rng,
- po::variables_map vm);
-};
-
-class SelectErasurePlugin
- : public ProgramOptionSelector<std::string_view,
- io_sequence::tester::pluginListSize,
- io_sequence::tester::pluginChoices> {
- public:
- SelectErasurePlugin(ceph::util::random_number_generator<int>& rng,
- po::variables_map vm);
-};
-
-class SelectErasureChunkSize
- : public ProgramOptionSelector<uint64_t, io_sequence::tester::chunkSizeSize,
- io_sequence::tester::chunkSizeChoices> {
- public:
- SelectErasureChunkSize(ceph::util::random_number_generator<int>& rng,
- po::variables_map vm);
+ select() override;
};
-class SelectECPool
- : public ProgramOptionSelector<std::string, 0,
- io_sequence::tester::poolChoices> {
+class SelectErasurePool : public ProgramOptionReader<std::string> {
public:
- SelectECPool(ceph::util::random_number_generator<int>& rng,
- po::variables_map vm, librados::Rados& rados, bool dry_run,
- bool allow_pool_autoscaling, bool allow_pool_balancer,
- bool allow_pool_deep_scrubbing, bool allow_pool_scrubbing,
- bool test_recovery);
- const std::string choose() override;
+ SelectErasurePool(ceph::util::random_number_generator<int>& rng,
+ po::variables_map vm,
+ librados::Rados& rados,
+ bool dry_run,
+ bool allow_pool_autoscaling,
+ bool allow_pool_balancer,
+ bool allow_pool_deep_scrubbing,
+ bool allow_pool_scrubbing,
+ bool test_recovery);
+ const std::string select() override;
bool get_allow_pool_autoscaling() { return allow_pool_autoscaling; }
bool get_allow_pool_balancer() { return allow_pool_balancer; }
int getChosenM() const { return m; }
private:
- void create_pool(librados::Rados& rados, const std::string& pool_name,
- const std::string& plugin, uint64_t chunk_size, int k,
- int m);
+ void create_pool( librados::Rados& rados,
+ const std::string& pool_name,
+ const std::string& plugin,
+ uint64_t chunk_size, int k,
+ int m);
protected:
librados::Rados& rados;
class TestObject {
public:
- TestObject(const std::string oid, librados::Rados& rados,
+ TestObject(const std::string oid,
+ librados::Rados& rados,
boost::asio::io_context& asio,
ceph::io_sequence::tester::SelectBlockSize& sbs,
- ceph::io_sequence::tester::SelectECPool& spl,
+ ceph::io_sequence::tester::SelectErasurePool& spl,
ceph::io_sequence::tester::SelectObjectSize& sos,
ceph::io_sequence::tester::SelectNumThreads& snt,
ceph::io_sequence::tester::SelectSeqRange& ssr,
- ceph::util::random_number_generator<int>& rng, ceph::mutex& lock,
- ceph::condition_variable& cond, bool dryrun, bool verbose,
- std::optional<int> seqseed, bool testRecovery);
+ ceph::util::random_number_generator<int>& rng,
+ ceph::mutex& lock,
+ ceph::condition_variable& cond,
+ bool dryrun,
+ bool verbose,
+ std::optional<int> seqseed,
+ bool testRecovery);
int get_num_io();
bool readyForIo();
ceph::io_sequence::tester::SelectBlockSize sbs;
ceph::io_sequence::tester::SelectObjectSize sos;
- ceph::io_sequence::tester::SelectECPool spo;
+ ceph::io_sequence::tester::SelectErasurePool spo;
ceph::io_sequence::tester::SelectNumThreads snt;
ceph::io_sequence::tester::SelectSeqRange ssr;
void help();
void list_sequence(bool testrecovery);
};
-} // namespace io_sequence::tester
+} // namespace tester
+} // namespace io_sequence
} // namespace ceph