From: Jon Bailey Date: Wed, 15 Jan 2025 13:12:40 +0000 (+0000) Subject: test/osd: Added support for new plugins X-Git-Tag: v20.3.0~425^2~1 X-Git-Url: http://git.apps.os.sepia.ceph.com/?a=commitdiff_plain;h=dd06be06eb25bc730be8f8196ed7c47ec82f5f81;p=ceph.git test/osd: Added support for new plugins Added new command line options for specifying plugins along with optional readers for them. Added arguments to JSON structures for some optional arguments to encode and decode more information about profiles. Added more choices for random selections of different plugin values and changed structure so profiles are created as part of a pool specifically, encapsulating them. Signed-off-by: Jon Bailey --- diff --git a/src/common/io_exerciser/EcIoSequence.cc b/src/common/io_exerciser/EcIoSequence.cc index 03732632c7b2c..c28008b5f05e3 100644 --- a/src/common/io_exerciser/EcIoSequence.cc +++ b/src/common/io_exerciser/EcIoSequence.cc @@ -1,5 +1,6 @@ #include "EcIoSequence.h" +#include #include using IoOp = ceph::io_exerciser::IoOp; @@ -11,7 +12,9 @@ using ReadInjectSequence = ceph::io_exerciser::ReadInjectSequence; bool EcIoSequence::is_supported(Sequence sequence) const { return true; } std::unique_ptr EcIoSequence::generate_sequence( - Sequence sequence, std::pair obj_size_range, int k, int m, + Sequence sequence, std::pair obj_size_range, + std::optional> km, + std::optional> mappingLayers, int seed) { switch (sequence) { case Sequence::SEQUENCE_SEQ0: @@ -42,9 +45,9 @@ std::unique_ptr EcIoSequence::generate_sequence( [[fallthrough]]; case Sequence::SEQUENCE_SEQ14: return std::make_unique(obj_size_range, seed, - sequence, k, m); + sequence, km, mappingLayers); case Sequence::SEQUENCE_SEQ10: - return std::make_unique(obj_size_range, seed, k, m); + return std::make_unique(obj_size_range, seed, km, mappingLayers); default: ceph_abort_msg("Unrecognised sequence"); } @@ -56,26 +59,99 @@ EcIoSequence::EcIoSequence(std::pair obj_size_range, int seed) clear_inject(false), shard_to_inject(std::nullopt) {} -void EcIoSequence::select_random_data_shard_to_inject_read_error(int k, int m) { - shard_to_inject = rng(k - 1); +void EcIoSequence::select_random_data_shard_to_inject_read_error( + std::optional> km, + std::optional> + mappingLayers) { + if (km) { + shard_to_inject = rng(km->first - 1); + } + if (mappingLayers) { + int count = std::ranges::count(mappingLayers->first, 'D'); + int dataShardPosition = rng(count - 1); + shard_to_inject = 0; + for (int i = 0; i < dataShardPosition; i++) { + shard_to_inject = + std::distance(std::find(mappingLayers->first.begin(), + mappingLayers->first.end(), *shard_to_inject), + mappingLayers->first.begin()); + ceph_assert(*shard_to_inject != std::string::npos); + } + } setup_inject = true; } -void EcIoSequence::select_random_data_shard_to_inject_write_error(int k, - int m) { +void EcIoSequence::select_random_data_shard_to_inject_write_error( + std::optional> km, + std::optional> + mappingLayers) { // Write errors do not support injecting to the primary OSD - shard_to_inject = rng(1, k - 1); + if (km) { + shard_to_inject = rng(1, km->first - 1); + } + if (mappingLayers) { + int count = std::ranges::count(mappingLayers->first, 'D'); + if (mappingLayers->first[0] == 'D') { + count--; + } + int dataShardPosition = rng(1, count - 1); + shard_to_inject = 0; + for (int i = 1; i < dataShardPosition; i++) { + shard_to_inject = + std::distance(std::find(mappingLayers->first.begin(), + mappingLayers->first.end(), *shard_to_inject), + mappingLayers->first.begin()); + ceph_assert(*shard_to_inject != std::string::npos); + } + } setup_inject = true; } -void EcIoSequence::select_random_shard_to_inject_read_error(int k, int m) { - shard_to_inject = rng(k + m - 1); +void EcIoSequence::select_random_shard_to_inject_read_error( + std::optional> km, + std::optional> + mappingLayers) { + if (km) { + shard_to_inject = rng(km->first + km->second - 1); + } + if (mappingLayers) { + int count = std::ranges::count(mappingLayers->first, 'D'); + int dataShardPosition = rng(count - 1); + shard_to_inject = 0; + for (int i = 0; i < dataShardPosition; i++) { + shard_to_inject = + std::distance(std::find(mappingLayers->first.begin(), + mappingLayers->first.end(), *shard_to_inject), + mappingLayers->first.begin()); + ceph_assert(*shard_to_inject != std::string::npos); + } + } setup_inject = true; } -void EcIoSequence::select_random_shard_to_inject_write_error(int k, int m) { +void EcIoSequence::select_random_shard_to_inject_write_error( + std::optional> km, + std::optional> + mappingLayers) { // Write errors do not support injecting to the primary OSD - shard_to_inject = rng(1, k + m - 1); + if (km) { + shard_to_inject = rng(1, km->first + km->second - 1); + } + if (mappingLayers) { + int count = std::ranges::count(mappingLayers->first, 'D'); + if (mappingLayers->first[0] == 'D') { + count--; + } + int dataShardPosition = rng(count - 1); + shard_to_inject = 0; + for (int i = 0; i < dataShardPosition; i++) { + shard_to_inject = + std::distance(std::find(mappingLayers->first.begin(), + mappingLayers->first.end(), *shard_to_inject), + mappingLayers->first.begin()); + ceph_assert(*shard_to_inject != std::string::npos); + } + } setup_inject = true; } @@ -92,10 +168,14 @@ void EcIoSequence::generate_random_write_inject_type() { } ceph::io_exerciser::ReadInjectSequence::ReadInjectSequence( - std::pair obj_size_range, int seed, Sequence s, int k, int m) + std::pair obj_size_range, + int seed, + Sequence s, + std::optional> km, + std::optional> mappingLayers) : EcIoSequence(obj_size_range, seed) { child_sequence = IoSequence::generate_sequence(s, obj_size_range, seed); - select_random_data_shard_to_inject_read_error(k, m); + select_random_data_shard_to_inject_read_error(km, mappingLayers); generate_random_read_inject_type(); } @@ -104,9 +184,13 @@ Sequence ceph::io_exerciser::ReadInjectSequence::get_id() const { } std::string ceph::io_exerciser::ReadInjectSequence::get_name() const { + std::string injected_shard = "UNKNOWN"; + if (shard_to_inject) { + injected_shard = std::to_string(*shard_to_inject); + } + return child_sequence->get_name() + - " running with read errors injected on shard " + - std::to_string(*shard_to_inject); + " running with read errors injected on shard " + injected_shard; } std::unique_ptr ReadInjectSequence::next() { @@ -124,6 +208,7 @@ std::unique_ptr ReadInjectSequence::next() { case OpType::Remove: nextOp.swap(childOp); switch (inject_op_type) { + ceph_assert(shard_to_inject.has_value()); case InjectOpType::ReadEIO: return ClearReadErrorInjectOp::generate(*shard_to_inject, 0); case InjectOpType::ReadMissingShard: @@ -179,8 +264,10 @@ ceph::io_exerciser::ReadInjectSequence::_next() { return DoneOp::generate(); } -ceph::io_exerciser::Seq10::Seq10(std::pair obj_size_range, int seed, - int k, int m) +ceph::io_exerciser::Seq10::Seq10( + std::pair obj_size_range, int seed, + std::optional> km, + std::optional> mappingLayers) : EcIoSequence(obj_size_range, seed), offset(0), length(1), @@ -192,7 +279,7 @@ ceph::io_exerciser::Seq10::Seq10(std::pair obj_size_range, int seed, test_all_sizes( false) // Only test obj_size(rand()) due to time constraints { - select_random_shard_to_inject_write_error(k, m); + select_random_shard_to_inject_write_error(km, mappingLayers); // We will inject specifically as part of our sequence in this sequence setup_inject = false; if (!test_all_sizes) { diff --git a/src/common/io_exerciser/EcIoSequence.h b/src/common/io_exerciser/EcIoSequence.h index 37283b3906bda..33d46fbbe41ab 100644 --- a/src/common/io_exerciser/EcIoSequence.h +++ b/src/common/io_exerciser/EcIoSequence.h @@ -1,3 +1,5 @@ +#pragma once + #include "IoSequence.h" namespace ceph { @@ -6,7 +8,11 @@ class EcIoSequence : public IoSequence { public: virtual bool is_supported(Sequence sequence) const override; static std::unique_ptr generate_sequence( - Sequence s, std::pair obj_size_range, int k, int m, int seed); + Sequence s, std::pair obj_size_range, + std::optional> km, + std::optional> + mappingLayers, + int seed); protected: bool setup_inject; @@ -18,18 +24,33 @@ class EcIoSequence : public IoSequence { // Writes cannot be sent to injected on shard zero, so selections seperated // out - void select_random_data_shard_to_inject_read_error(int k, int m); - void select_random_data_shard_to_inject_write_error(int k, int m); - void select_random_shard_to_inject_read_error(int k, int m); - void select_random_shard_to_inject_write_error(int k, int m); + void select_random_data_shard_to_inject_read_error( + std::optional> km, + std::optional> + mappingLayers); + void select_random_data_shard_to_inject_write_error( + std::optional> km, + std::optional> + mappingLayers); + void select_random_shard_to_inject_read_error( + std::optional> km, + std::optional> + mappingLayers); + void select_random_shard_to_inject_write_error( + std::optional> km, + std::optional> + mappingLayers); void generate_random_read_inject_type(); void generate_random_write_inject_type(); }; class ReadInjectSequence : public EcIoSequence { public: - ReadInjectSequence(std::pair obj_size_range, int seed, Sequence s, - int k, int m); + ReadInjectSequence( + std::pair obj_size_range, int seed, Sequence s, + std::optional> km, + std::optional> + mappingLayers); Sequence get_id() const override; std::string get_name() const override; @@ -43,7 +64,10 @@ class ReadInjectSequence : public EcIoSequence { class Seq10 : public EcIoSequence { public: - Seq10(std::pair obj_size_range, int seed, int k, int m); + Seq10(std::pair obj_size_range, int seed, + std::optional> km, + std::optional> + mappingLayers); Sequence get_id() const override; std::string get_name() const override; diff --git a/src/common/json/OSDStructures.cc b/src/common/json/OSDStructures.cc index aaac5f6e16938..f9e3d8d75b6ba 100644 --- a/src/common/json/OSDStructures.cc +++ b/src/common/json/OSDStructures.cc @@ -85,12 +85,17 @@ void OSDECProfileGetReply::dump(Formatter* f) const { encode_json("crush-osds-per-failure-domain", crush_osds_per_failure_domain, f); encode_json("crush-root", crush_root, f); - encode_json("jerasure-per-chunk-alignment", jerasure_per_chunk_alignment, f); + encode_json("plugin", plugin, f); encode_json("k", k, f); encode_json("m", m, f); - encode_json("plugin", plugin, f); - encode_json("technique", technique, f); + encode_json("l", l, f); encode_json("w", w, f); + encode_json("c", c, f); + encode_json("packetsize", packetsize, f); + encode_json("technique", technique, f); + encode_json("layers", layers, f); + encode_json("mapping", mapping, f); + encode_json("jerasure-per-chunk-alignment", jerasure_per_chunk_alignment, f); } void OSDECProfileGetReply::decode_json(JSONObj* obj) { @@ -101,24 +106,31 @@ void OSDECProfileGetReply::decode_json(JSONObj* obj) { JSONDecoder::decode_json("crush-osds-per-failure-domain", crush_osds_per_failure_domain, obj); JSONDecoder::decode_json("crush-root", crush_root, obj); - JSONDecoder::decode_json("jerasure-per-chunk-alignment", - jerasure_per_chunk_alignment, obj); + JSONDecoder::decode_json("plugin", plugin, obj); JSONDecoder::decode_json("k", k, obj); JSONDecoder::decode_json("m", m, obj); - JSONDecoder::decode_json("plugin", plugin, obj); - JSONDecoder::decode_json("technique", technique, obj); + JSONDecoder::decode_json("l", l, obj); JSONDecoder::decode_json("w", w, obj); + JSONDecoder::decode_json("c", c, obj); + JSONDecoder::decode_json("packetsize", packetsize, obj); + JSONDecoder::decode_json("technique", technique, obj); + JSONDecoder::decode_json("layers", layers, obj); + JSONDecoder::decode_json("mapping", mapping, obj); + JSONDecoder::decode_json("jerasure-per-chunk-alignment", + jerasure_per_chunk_alignment, obj); } void OSDECProfileSetRequest::dump(Formatter* f) const { encode_json("prefix", "osd erasure-code-profile set", f); encode_json("name", name, f); encode_json("profile", profile, f); + encode_json("force", force, f); } void OSDECProfileSetRequest::decode_json(JSONObj* obj) { JSONDecoder::decode_json("name", name, obj); JSONDecoder::decode_json("profile", profile, obj); + JSONDecoder::decode_json("force", force, obj); } void OSDECPoolCreateRequest::dump(Formatter* f) const { diff --git a/src/common/json/OSDStructures.h b/src/common/json/OSDStructures.h index 3e4528a099fc0..4fc3e307e2aaf 100644 --- a/src/common/json/OSDStructures.h +++ b/src/common/json/OSDStructures.h @@ -69,12 +69,17 @@ struct OSDECProfileGetReply { int crush_num_failure_domains; int crush_osds_per_failure_domain; std::string crush_root; - bool jerasure_per_chunk_alignment; + std::string plugin; int k; int m; - std::string plugin; - std::string technique; - std::string w; + std::optional l; + std::optional w; + std::optional c; + std::optional packetsize; + std::optional technique; + std::optional layers; + std::optional mapping; + std::optional jerasure_per_chunk_alignment; void dump(Formatter* f) const; void decode_json(JSONObj* obj); @@ -83,6 +88,7 @@ struct OSDECProfileGetReply { struct OSDECProfileSetRequest { std::string name; std::vector profile; + bool force; void dump(Formatter* f) const; void decode_json(JSONObj* obj); diff --git a/src/test/osd/ceph_test_rados_io_sequence/ProgramOptionReader.h b/src/test/osd/ceph_test_rados_io_sequence/ProgramOptionReader.h index e226203e46d1d..12d926aefdc50 100644 --- a/src/test/osd/ceph_test_rados_io_sequence/ProgramOptionReader.h +++ b/src/test/osd/ceph_test_rados_io_sequence/ProgramOptionReader.h @@ -1,10 +1,9 @@ #pragma once +#include #include #include -#include - #include "include/ceph_assert.h" #include "include/random.h" @@ -19,6 +18,12 @@ * 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. * + * class OptionalProgramOptionReader + * Redefinition of ProgramOptionalReader that returns the result as a + * std::optional of the return type. Shorthand for specifying the above + * with a second return type of optional for + * all optional implementations + * * ProgramOptionSelector * Implementation of the above ProgramOptionReader. * This is constructed with a list of options and implements the select @@ -29,6 +34,13 @@ * This takes an random_number_generator so that randomness can be controlled * and reproduced if desired. * + * ProgramOptionGeneratedSelector + * A class for selecting an option from a list of options, similar to above. + * This implements a pure virtual method generate_selections() which should be + * overriden so generation code can be added to generate the selection list at + * runtime, whereas ProgramOptionSelector relies on the list existing at + * compile time. + * */ namespace po = boost::program_options; @@ -60,6 +72,14 @@ class ProgramOptionReader { std::string option_name; }; +template +class OptionalProgramOptionReader + : public ProgramOptionReader> { + public: + using ProgramOptionReader>::ProgramOptionReader; +}; + template { std::optional first_value; }; -} // namespace io_sequence + +template +class ProgramOptionGeneratedSelector + : public OptionalProgramOptionReader { + public: + ProgramOptionGeneratedSelector(ceph::util::random_number_generator& rng, + po::variables_map& vm, + const std::string& option_name, + bool first_use) + : OptionalProgramOptionReader(vm, option_name), + rng(rng), + first_use(first_use) {} + + const std::optional select() final { + if (this->force_value.has_value()) + return *this->force_value; + else if (first_use) + return selectFirst(); + else + return selectRandom(); + } + + protected: + virtual const std::vector generate_selections() = 0; + virtual const std::optional selectFirst() { + first_use = false; + std::vector selection = generate_selections(); + if (selection.size() > 0) + return selection[0]; + else + return std::nullopt; + } + + virtual const std::optional selectRandom() { + std::vector selection = generate_selections(); + if (selection.size() > 0) + return selection[rng(selection.size() - 1)]; + else + return std::nullopt; + } + + bool is_first_use() { return first_use; } + + private: + ceph::util::random_number_generator& rng; + + bool first_use; +}; } // namespace tester +} // namespace io_sequence } // namespace ceph \ No newline at end of file diff --git a/src/test/osd/ceph_test_rados_io_sequence/ceph_test_rados_io_sequence.cc b/src/test/osd/ceph_test_rados_io_sequence/ceph_test_rados_io_sequence.cc index 7f7a10f649e46..d645edbd0f134 100644 --- a/src/test/osd/ceph_test_rados_io_sequence/ceph_test_rados_io_sequence.cc +++ b/src/test/osd/ceph_test_rados_io_sequence/ceph_test_rados_io_sequence.cc @@ -2,6 +2,7 @@ #include #include +#include #include #include "common/Formatter.h" @@ -10,11 +11,6 @@ #include "common/ceph_json.h" #include "common/debug.h" #include "common/dout.h" -#include "common/split.h" -#include "common/strtol.h" // for strict_iecstrtoll() -#include "common/ceph_json.h" -#include "common/Formatter.h" - #include "common/io_exerciser/DataGenerator.h" #include "common/io_exerciser/EcIoSequence.h" #include "common/io_exerciser/IoOp.h" @@ -25,6 +21,9 @@ #include "common/json/BalancerStructures.h" #include "common/json/ConfigStructures.h" #include "common/json/OSDStructures.h" +#include "common/split.h" +#include "common/strtol.h" // for strict_iecstrtoll() +#include "erasure-code/ErasureCodePlugin.h" #include "fmt/format.h" #include "global/global_context.h" #include "global/global_init.h" @@ -159,12 +158,18 @@ po::options_description get_options_description() { "seed", po::value(), "seed for whole test")( "seqseed", po::value(), "seed for sequence")( "blocksize,b", po::value(), "block size (default 2048)")( - "chunksize,c", po::value(), "chunk size (default 4096)")( - "pool,p", po::value(), "pool name")( + "pool,p", po::value(), "existing pool name")( + "profile", po::value(), "existing profile name")( "object,o", po::value()->default_value("test"), - "object name")("km", po::value(), - "k,m EC pool profile (default 2,2)")( - "plugin", po::value(), "EC plugin (isa or jerasure)")( + "object name")("plugin", po::value(), "EC plugin")( + "chunksize,c", po::value(), "chunk size (default 4096)")( + "km", po::value(), "k,m EC pool profile (default 2,2)")( + "technique", po::value(), "EC profile technique")( + "packetsize", po::value(), "Jerasure EC profile packetsize")( + "w", po::value(), "Jerasure EC profile w value")( + "c", po::value(), "Shec EC profile c value")( + "mapping", po::value(), "LRC EC profile mapping")( + "layers", po::value(), "LRC EC profile layers")( "objectsize", po::value(), "min,max object size in blocks (default 1,32)")( "threads,t", po::value(), @@ -259,11 +264,499 @@ ceph::io_sequence::tester::SelectSeqRange::select() { } } +ceph::io_sequence::tester::SelectErasureTechnique::SelectErasureTechnique( + ceph::util::random_number_generator& rng, + po::variables_map& vm, + std::string_view plugin, + bool first_use) + : ProgramOptionGeneratedSelector(rng, vm, "technique", + first_use), + rng(rng), + plugin(plugin) {} + +const std::vector +ceph::io_sequence::tester::SelectErasureTechnique::generate_selections() { + std::vector techniques = {}; + if (plugin == "jerasure") { + techniques.push_back("reed_sol_van"); + techniques.push_back("reed_sol_r6_op"); + techniques.push_back("cauchy_orig"); + techniques.push_back("cauchy_good"); + techniques.push_back("liberation"); + techniques.push_back("blaum_roth"); + techniques.push_back("liber8tion"); + } else if (plugin == "isa") { + techniques.push_back("reed_sol_van"); + techniques.push_back("cauchy"); + } else if (plugin == "shec") { + techniques.push_back("single"); + techniques.push_back("multiple"); + } + + return techniques; +} + +ceph::io_sequence::tester::lrc::SelectMappingAndLayers::SelectMappingAndLayers( + ceph::util::random_number_generator& rng, + po::variables_map& vm, + bool first_use) + : rng_seed(rng()), + mapping_rng{rng_seed}, + layers_rng{rng_seed}, + sma{mapping_rng, vm, "mapping", first_use}, + sly{layers_rng, vm, "layers", first_use} { + if (sma.isForced() != sly.isForced()) { + ceph_abort_msg("Mapping and layers must be used together when one is used"); + } +} + +const std::pair +ceph::io_sequence::tester::lrc::SelectMappingAndLayers::select() { + return std::pair(sma.select(), sly.select()); +} + +ceph::io_sequence::tester::SelectErasureKM::SelectErasureKM( + ceph::util::random_number_generator& rng, + po::variables_map& vm, + std::string_view plugin, + const std::optional& technique, + bool first_use) + : ProgramOptionGeneratedSelector>(rng, vm, "km", + first_use), + rng(rng), + plugin(plugin), + technique(technique) {} + +const std::vector> +ceph::io_sequence::tester::SelectErasureKM::generate_selections() { + std::vector> selection; + + // Gives different spreads of k and m depending on the plugin and technique + if (plugin == "isa" || plugin == "clay" || + (plugin == "jerasure" && + (technique == "reed_sol_van" || technique == "cauchy_orig" || + technique == "cauchy_good" || technique == std::nullopt))) { + for (int m = 1; m <= 3; m++) + for (int k = 2; k <= 6; k++) selection.push_back({k, m}); + } else if (plugin == "shec" || + (plugin == "jerasure" && + (technique == "liberation" || technique == "blaum_roth"))) { + for (int m = 1; m <= 2; m++) + for (int k = 2; k <= 6; k++) selection.push_back({k, m}); + } else if (plugin == "jerasure" && + (technique == "reed_sol_r6_op" || technique == "liber8tion")) { + for (int k = 2; k <= 6; k++) selection.push_back({k, 2}); + } + + // We want increased chances of these as we will test with c=1 and c=2 + if (plugin == "shec") + for (int i = 0; i < 2; i++) + for (int k = 3; k <= 6; k++) selection.push_back({k, 3}); + + // Add extra miscelaneous interesting options for testing w values + if (plugin == "jerasure") { + if (technique == "reed_sol_van") + // Double chance of chosing to test more w values + for (int i = 0; i < 2; i++) selection.push_back({6, 3}); + + if (technique == "liberation" || technique == "blaum_roth") + // Double chance of chosing to test more different w values + for (int i = 0; i < 2; i++) selection.push_back({6, 2}); + + if (technique == "liber8tion") selection.push_back({2, 2}); + } + + return selection; +} + +ceph::io_sequence::tester::jerasure::SelectErasureW::SelectErasureW( + ceph::util::random_number_generator& rng, + po::variables_map& vm, + std::string_view plugin, + const std::optional& technique, + const std::optional>& km, + const std::optional& packetsize, + bool first_use) + : ProgramOptionGeneratedSelector(rng, vm, "w", first_use), + rng(rng), + plugin(plugin), + km(km), + packetsize(packetsize) {} + +const std::vector +ceph::io_sequence::tester::jerasure::SelectErasureW::generate_selections() { + std::vector selection = {}; + + if (plugin != "jerasure") { + return selection; + } + + if (technique && km && technique == "reed_sol_van" && km->first == 6 && + km->second == 3) { + selection.push_back(16); + selection.push_back(32); + } + + if (km && km->first == 6 && km->second == 2) { + if (technique && technique == "liberation") { + if (packetsize == 32) selection.push_back(11); + if (packetsize == 36) selection.push_back(13); + } else if (technique && technique == "blaum_roth") { + if (packetsize == 44) selection.push_back(7); + if (packetsize == 60) selection.push_back(10); + } + } + + return selection; +} + +ceph::io_sequence::tester::shec::SelectErasureC::SelectErasureC( + ceph::util::random_number_generator& rng, + po::variables_map& vm, + std::string_view plugin, + const std::optional>& km, + bool first_use) + : ProgramOptionGeneratedSelector(rng, vm, "c", first_use), + rng(rng), + plugin(plugin), + km(km) {} + +const std::vector +ceph::io_sequence::tester::shec::SelectErasureC::generate_selections() { + if (plugin != "shec") { + return {}; + } + + std::vector selection = {}; + selection.push_back(1); + + if (km && km->first == 3 && km->second >= 3) { + selection.push_back(2); + } + + return selection; +} + +ceph::io_sequence::tester::jerasure::SelectErasurePacketSize:: + SelectErasurePacketSize(ceph::util::random_number_generator& rng, + po::variables_map& vm, + std::string_view plugin, + const std::optional& technique, + const std::optional>& km, + bool first_use) + : ProgramOptionGeneratedSelector(rng, vm, "packetsize", + first_use), + rng(rng), + plugin(plugin), + technique(technique), + km(km) {} + +const std::vector ceph::io_sequence::tester::jerasure:: + SelectErasurePacketSize::generate_selections() { + std::vector selection = {}; + + if (plugin != "jerasure") { + return selection; + } + + if (technique == "cauchy_orig" ||technique == "cauchy_good" || + technique == "liberation" || technique == "blaum_roth" || + technique == "liber8tion") { + selection.push_back(32); + } + + if (km && technique && technique == "liberation" && km->first == 6 && + km->second == 2) { + selection.push_back(32); + selection.push_back(36); + } + + if (km && technique && technique == "blaum_roth" && km->first == 6 && + km->second == 2) { + selection.push_back(44); + selection.push_back(60); + } + + if (km && technique && technique == "liber8tion" && km->first == 6 && + km->second == 2) { + selection.push_back(92); + } + + return selection; +} + +ceph::io_sequence::tester::SelectErasureChunkSize::SelectErasureChunkSize( + ceph::util::random_number_generator& rng, + po::variables_map& vm, + ErasureCodeInterfaceRef ec_impl, + bool first_use) + : ProgramOptionGeneratedSelector(rng, vm, "chunksize", first_use), + rng(rng), + ec_impl(ec_impl) {} + +const std::vector +ceph::io_sequence::tester::SelectErasureChunkSize::generate_selections() { + int minimum_granularity = ec_impl->get_minimum_granularity(); + int data_chunks = ec_impl->get_data_chunk_count(); + int minimum_chunksize = + ec_impl->get_chunk_size(minimum_granularity * data_chunks); + + std::vector choices = {}; + + if (4096 % minimum_chunksize == 0) { + choices.push_back(4096); + } else { + choices.push_back(minimum_chunksize * rng(4)); + } + + if ((64 * 1024) % minimum_chunksize == 0) { + choices.push_back(64 * 1024); + } else { + choices.push_back(minimum_chunksize * rng(64)); + } + + if ((256 * 1024) % minimum_chunksize == 0) { + choices.push_back(256 * 1024); + } else { + choices.push_back(minimum_chunksize * rng(256)); + } + + return choices; +} + +ceph::io_sequence::tester::SelectErasureProfile::SelectErasureProfile( + boost::intrusive_ptr cct, + ceph::util::random_number_generator& rng, + po::variables_map& vm, + librados::Rados& rados, + bool dry_run, + bool first_use) + : ProgramOptionReader(vm, "profile"), + cct(cct), + rados(rados), + dry_run(dry_run), + rng(rng), + vm(vm), + first_use(first_use), + spl{rng, vm, "plugin", true}, + sml{rng, vm, true} { + if (isForced()) { + std::array disallowed_options = { + "pool", "km", "technique", "packetsize", "c", + "w", "mapping", "layers", "chunksize"}; + + for (std::string& option : disallowed_options) { + if (vm.count(option) > 0) { + ceph_abort_msg( + fmt::format("{} option not allowed " + "if profile is specified", + option)); + } + } + } +} + +const ceph::io_sequence::tester::Profile +ceph::io_sequence::tester::SelectErasureProfile::select() { + ceph::io_sequence::tester::Profile profile; + + if (force_value) { + if (!dry_run) { + profile = selectExistingProfile(force_value->name); + } + } else { + profile.plugin = spl.select(); + + SelectErasureTechnique set{rng, vm, profile.plugin, first_use}; + profile.technique = set.select(); + + SelectErasureKM skm{rng, vm, profile.plugin, profile.technique, first_use}; + profile.km = skm.select(); + + jerasure::SelectErasurePacketSize sps{ + rng, vm, profile.plugin, profile.technique, profile.km, first_use}; + profile.packet_size = sps.select(); + + if (profile.plugin == "jerasure") { + jerasure::SelectErasureW ssw{rng, + vm, + profile.plugin, + profile.technique, + profile.km, + profile.packet_size, + first_use}; + profile.w = ssw.select(); + } else if (profile.plugin == "shec") { + shec::SelectErasureC ssc{rng, vm, profile.plugin, profile.km, first_use}; + profile.c = ssc.select(); + } else if (profile.plugin == "lrc") { + std::pair mappingAndLayers = sml.select(); + profile.mapping = mappingAndLayers.first; + profile.layers = mappingAndLayers.second; + } + + ErasureCodeProfile erasureCodeProfile; + erasureCodeProfile["plugin"] = std::string(profile.plugin); + if (profile.km) { + erasureCodeProfile["k"] = std::to_string(profile.km->first); + erasureCodeProfile["m"] = std::to_string(profile.km->second); + } + if (profile.technique) { + erasureCodeProfile["technique"] = *profile.technique; + } + if (profile.packet_size) { + erasureCodeProfile["packetsize"] = std::to_string(*profile.packet_size); + } + if (profile.c) { + erasureCodeProfile["c"] = std::to_string(*profile.c); + } + if (profile.w) { + erasureCodeProfile["packetsize"] = std::to_string(*profile.w); + } + if (profile.jerasure_per_chunk_alignment) { + erasureCodeProfile["jerasure_per_chunk_alignment"] = + std::to_string(*profile.jerasure_per_chunk_alignment); + } + if (profile.mapping) { + erasureCodeProfile["mapping"] = *profile.mapping; + } + if (profile.layers) { + erasureCodeProfile["layers"] = *profile.layers; + } + + ErasureCodePluginRegistry& instance = ErasureCodePluginRegistry::instance(); + ErasureCodeInterfaceRef ec_impl; + std::stringstream ss; + instance.factory(std::string(profile.plugin), + cct->_conf.get_val("erasure_code_dir"), + erasureCodeProfile, &ec_impl, &ss); + ceph_assert(ec_impl); + + SelectErasureChunkSize scs{rng, vm, ec_impl, first_use}; + profile.chunk_size = scs.select(); + + profile.name = fmt::format("testprofile_pl{}", profile.plugin); + if (profile.technique) { + profile.name += fmt::format("_t{}", profile.technique); + } + if (profile.km) { + profile.name += + fmt::format("_k{}_m{}", profile.km->first, profile.km->second); + } + if (profile.packet_size) { + profile.name += fmt::format("_ps{}", *profile.packet_size); + } + if (profile.c) { + profile.name += fmt::format("_c{}", *profile.c); + } + if (profile.w) { + profile.name += fmt::format("_w{}", *profile.w); + } + if (profile.chunk_size) { + profile.name += fmt::format("_cs{}", *profile.chunk_size); + } + if (profile.mapping) { + profile.name += fmt::format("_ma{}", *profile.mapping); + } + + if (!dry_run) { + create(profile); + } + } + + first_use = false; + + return profile; +} + +void ceph::io_sequence::tester::SelectErasureProfile::create( + const ceph::io_sequence::tester::Profile& profile) { + bufferlist inbl, outbl; + auto formatter = std::make_unique(false); + + std::vector profile_values = { + fmt::format("plugin={}", profile.plugin)}; + + if (profile.km) { + profile_values.push_back(fmt::format("k={}", profile.km->first)); + profile_values.push_back(fmt::format("m={}", profile.km->second)); + } + if (profile.technique) + profile_values.push_back(fmt::format("technique={}", profile.technique)); + if (profile.packet_size) + profile_values.push_back(fmt::format("packetsize={}", profile.packet_size)); + if (profile.c) profile_values.push_back(fmt::format("c={}", profile.c)); + if (profile.w) profile_values.push_back(fmt::format("w={}", profile.w)); + if (profile.mapping) + profile_values.push_back(fmt::format("mapping={}", profile.mapping)); + if (profile.layers) + profile_values.push_back(fmt::format("layers={}", profile.layers)); + if (profile.chunk_size) + profile_values.push_back(fmt::format("stripe_unit={}", profile.chunk_size)); + + // Crush-failure-domain only seems to be taken into account when specifying + // k and m values in LRC, so we set a crush step to do the same, which is + // what LRC does under the covers + if (profile.plugin == "lrc") + profile_values.push_back("crush-steps=[[\"chooseleaf\",\"osd\",0]]"); + else + profile_values.push_back("crush-failure-domain=osd"); + + bool force = + profile.chunk_size.has_value() && (*(profile.chunk_size) % 4096 != 0); + ceph::messaging::osd::OSDECProfileSetRequest ecProfileSetRequest{ + profile.name, profile_values, force}; + int rc = + send_mon_command(ecProfileSetRequest, rados, "OSDECProfileSetRequest", + inbl, &outbl, formatter.get()); + ceph_assert(rc == 0); +} + +const ceph::io_sequence::tester::Profile +ceph::io_sequence::tester::SelectErasureProfile::selectExistingProfile( + const std::string& profile_name) { + int rc; + bufferlist inbl, outbl; + auto formatter = std::make_shared(false); + + ceph::messaging::osd::OSDECProfileGetRequest osdECProfileGetRequest{ + profile_name}; + rc = send_mon_command(osdECProfileGetRequest, rados, "OSDECProfileGetRequest", + inbl, &outbl, formatter.get()); + ceph_assert(rc == 0); + + JSONParser p; + bool success = p.parse(outbl.c_str(), outbl.length()); + ceph_assert(success); + + ceph::messaging::osd::OSDECProfileGetReply reply; + reply.decode_json(&p); + + ceph::io_sequence::tester::Profile profile{}; + profile.name = profile_name; + profile.plugin = reply.plugin; + profile.km = {reply.k, reply.m}; + profile.technique = reply.technique->c_str(); + profile.packet_size = reply.packetsize; + profile.c = reply.c; + profile.w = reply.w; + profile.mapping = reply.mapping; + profile.layers = reply.layers; + + return profile; +} + ceph::io_sequence::tester::SelectErasurePool::SelectErasurePool( - ceph::util::random_number_generator& 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) + boost::intrusive_ptr cct, + ceph::util::random_number_generator& 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) : ProgramOptionReader(vm, "pool"), rados(rados), dry_run(dry_run), @@ -272,90 +765,90 @@ ceph::io_sequence::tester::SelectErasurePool::SelectErasurePool( allow_pool_deep_scrubbing(allow_pool_deep_scrubbing), allow_pool_scrubbing(allow_pool_scrubbing), test_recovery(test_recovery), - 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(); + first_use(true), + sep{cct, rng, vm, rados, dry_run, first_use} { + if (isForced()) { + std::array disallowed_options = { + "profile", "km", "technique", "packetsize", "c", + "w", "mapping", "layers", "chunksize"}; + + for (std::string& option : disallowed_options) { + if (vm.count(option) > 0) { + ceph_abort_msg( + fmt::format("{} option not allowed " + "if pool is specified", + option)); + } } } } const std::string ceph::io_sequence::tester::SelectErasurePool::select() { - std::pair value; - if (!skm.isForced() && force_value.has_value()) { - int rc; - bufferlist inbl, outbl; - auto formatter = std::make_unique(false); + first_use = true; - ceph::messaging::osd::OSDPoolGetRequest osdPoolGetRequest{*force_value}; - rc = send_mon_command(osdPoolGetRequest, rados, "OSDPoolGetRequest", inbl, - &outbl, formatter.get()); - ceph_assert(rc == 0); - - JSONParser p; - bool success = p.parse(outbl.c_str(), outbl.length()); - ceph_assert(success); + std::string created_pool_name = ""; + if (!dry_run) { + if (isForced()) { + int rc; + bufferlist inbl, outbl; + auto formatter = std::make_shared(false); + + ceph::messaging::osd::OSDPoolGetRequest osdPoolGetRequest{*force_value}; + rc = send_mon_command(osdPoolGetRequest, rados, "OSDPoolGetRequest", inbl, + &outbl, formatter.get()); + ceph_assert(rc == 0); - ceph::messaging::osd::OSDPoolGetReply osdPoolGetReply; - osdPoolGetReply.decode_json(&p); + JSONParser p; + bool success = p.parse(outbl.c_str(), outbl.length()); + ceph_assert(success); - ceph::messaging::osd::OSDECProfileGetRequest osdECProfileGetRequest{ - osdPoolGetReply.erasure_code_profile}; - rc = send_mon_command(osdECProfileGetRequest, rados, - "OSDECProfileGetRequest", inbl, &outbl, - formatter.get()); - ceph_assert(rc == 0); + ceph::messaging::osd::OSDPoolGetReply osdPoolGetReply; + osdPoolGetReply.decode_json(&p); - success = p.parse(outbl.c_str(), outbl.length()); - ceph_assert(success); + profile = sep.selectExistingProfile(osdPoolGetReply.erasure_code_profile); + } else { + created_pool_name = create(); + } - ceph::messaging::osd::OSDECProfileGetReply reply; - reply.decode_json(&p); - k = reply.k; - m = reply.m; - return *force_value; - } else { - value = skm.select(); + if (!dry_run) { + configureServices(allow_pool_autoscaling, allow_pool_balancer, + allow_pool_deep_scrubbing, allow_pool_scrubbing, + test_recovery); + } } - k = value.first; - m = value.second; - 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); - if (!dry_run) { - create_pool(rados, pool_name, plugin, chunk_size, k, m); - } - return pool_name; + return force_value.value_or(created_pool_name); } -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) { +std::string ceph::io_sequence::tester::SelectErasurePool::create() { int rc; bufferlist inbl, outbl; - auto formatter = std::make_unique(false); + auto formatter = std::make_shared(false); - ceph::messaging::osd::OSDECProfileSetRequest ecProfileSetRequest{ - fmt::format("testprofile-{}", pool_name), - {fmt::format("plugin={}", plugin), fmt::format("k={}", k), - fmt::format("m={}", m), fmt::format("stripe_unit={}", chunk_size), - fmt::format("crush-failure-domain=osd")}}; - rc = send_mon_command(ecProfileSetRequest, rados, "OSDECProfileSetRequest", - inbl, &outbl, formatter.get()); - ceph_assert(rc == 0); + std::string pool_name; + profile = sep.select(); + pool_name = fmt::format("testpool-pr{}", profile->name); ceph::messaging::osd::OSDECPoolCreateRequest poolCreateRequest{ - pool_name, "erasure", 8, 8, fmt::format("testprofile-{}", pool_name)}; + pool_name, "erasure", 8, 8, profile->name}; rc = send_mon_command(poolCreateRequest, rados, "OSDECPoolCreateRequest", inbl, &outbl, formatter.get()); ceph_assert(rc == 0); - if (allow_pool_autoscaling) { + return pool_name; +} + +void ceph::io_sequence::tester::SelectErasurePool::configureServices( + bool allow_pool_autoscaling, + bool allow_pool_balancer, + bool allow_pool_deep_scrubbing, + bool allow_pool_scrubbing, + bool test_recovery) { + int rc; + bufferlist inbl, outbl; + auto formatter = std::make_shared(false); + + if (!allow_pool_autoscaling) { ceph::messaging::osd::OSDSetRequest setNoAutoscaleRequest{"noautoscale", std::nullopt}; rc = send_mon_command(setNoAutoscaleRequest, rados, "OSDSetRequest", inbl, @@ -363,13 +856,13 @@ void ceph::io_sequence::tester::SelectErasurePool::create_pool( ceph_assert(rc == 0); } - if (allow_pool_balancer) { - ceph::messaging::balancer::BalancerOffRequest balancerOffRequest{}; + if (!allow_pool_balancer) { + ceph::messaging::balancer::BalancerOffRequest balancerOffRequest; rc = send_mon_command(balancerOffRequest, rados, "BalancerOffRequest", inbl, &outbl, formatter.get()); ceph_assert(rc == 0); - ceph::messaging::balancer::BalancerStatusRequest balancerStatusRequest{}; + ceph::messaging::balancer::BalancerStatusRequest balancerStatusRequest; rc = send_mon_command(balancerStatusRequest, rados, "BalancerStatusRequest", inbl, &outbl, formatter.get()); ceph_assert(rc == 0); @@ -427,8 +920,14 @@ ceph::io_sequence::tester::TestObject::TestObject( oid, sbs.select(), rng()); } else { const std::string pool = spo.select(); - poolK = spo.getChosenK(); - poolM = spo.getChosenM(); + if (!dryrun) { + ceph_assert(spo.getProfile()); + poolKM = spo.getProfile()->km; + if (spo.getProfile()->mapping && spo.getProfile()->layers) { + poolMappingLayers = {*spo.getProfile()->mapping, + *spo.getProfile()->layers}; + } + } int threads = snt.select(); @@ -467,7 +966,8 @@ ceph::io_sequence::tester::TestObject::TestObject( if (testrecovery) { seq = ceph::io_exerciser::EcIoSequence::generate_sequence( - curseq, obj_size_range, poolK, poolM, seqseed.value_or(rng())); + curseq, obj_size_range, poolKM, poolMappingLayers, + seqseed.value_or(rng())); } else { seq = ceph::io_exerciser::IoSequence::generate_sequence( curseq, obj_size_range, seqseed.value_or(rng())); @@ -505,7 +1005,8 @@ bool ceph::io_sequence::tester::TestObject::next() { } else { if (testrecovery) { seq = ceph::io_exerciser::EcIoSequence::generate_sequence( - curseq, obj_size_range, poolK, poolM, seqseed.value_or(rng())); + curseq, obj_size_range, poolKM, poolMappingLayers, + seqseed.value_or(rng())); } else { seq = ceph::io_exerciser::IoSequence::generate_sequence( curseq, obj_size_range, seqseed.value_or(rng())); @@ -528,14 +1029,17 @@ int ceph::io_sequence::tester::TestObject::get_num_io() { return exerciser_model->get_num_io(); } -ceph::io_sequence::tester::TestRunner::TestRunner(po::variables_map& vm, - librados::Rados& rados) +ceph::io_sequence::tester::TestRunner::TestRunner( + boost::intrusive_ptr cct, + po::variables_map& vm, + librados::Rados& rados) : rados(rados), seed(vm.contains("seed") ? vm["seed"].as() : time(nullptr)), rng(ceph::util::random_number_generator(seed)), sbs{rng, vm, "blocksize", true}, sos{rng, vm, "objectsize", true}, - spo{rng, + spo{cct, + rng, vm, rados, vm.contains("dryrun"), @@ -596,9 +1100,18 @@ void ceph::io_sequence::tester::TestRunner::list_sequence(bool testrecovery) { ceph::io_exerciser::Sequence s = ceph::io_exerciser::Sequence::SEQUENCE_BEGIN; std::unique_ptr seq; if (testrecovery) { + std::optional profile = + spo.getProfile(); + std::optional> km; + std::optional> mappingLayers; + if (profile) { + km = profile->km; + if (profile->mapping && profile->layers) { + mappingLayers = {*spo.getProfile()->mapping, *spo.getProfile()->layers}; + } + } seq = ceph::io_exerciser::EcIoSequence::generate_sequence( - s, obj_size_range, spo.getChosenK(), spo.getChosenM(), - seqseed.value_or(rng())); + s, obj_size_range, km, mappingLayers, seqseed.value_or(rng())); } else { seq = ceph::io_exerciser::IoSequence::generate_sequence( s, obj_size_range, seqseed.value_or(rng())); @@ -633,7 +1146,7 @@ std::string ceph::io_sequence::tester::TestRunner::get_token(bool allow_eof) { } std::optional -ceph::io_sequence::tester::TestRunner ::get_optional_token() { +ceph::io_sequence::tester::TestRunner::get_optional_token() { std::optional ret = std::nullopt; if (tokens != split.end()) { ret = std::string(*tokens++); @@ -652,7 +1165,7 @@ uint64_t ceph::io_sequence::tester::TestRunner::get_numeric_token() { } std::optional -ceph::io_sequence::tester::TestRunner ::get_optional_numeric_token() { +ceph::io_sequence::tester::TestRunner::get_optional_numeric_token() { std::string parse_error; std::optional token = get_optional_token(); if (token) { @@ -937,7 +1450,8 @@ int main(int argc, char** argv) { std::unique_ptr runner; try { - runner = std::make_unique(vm, rados); + runner = + std::make_unique(cct, vm, rados); } catch (const po::error& e) { return 1; } diff --git a/src/test/osd/ceph_test_rados_io_sequence/ceph_test_rados_io_sequence.h b/src/test/osd/ceph_test_rados_io_sequence/ceph_test_rados_io_sequence.h index 40826f0d85948..986b823104e49 100644 --- a/src/test/osd/ceph_test_rados_io_sequence/ceph_test_rados_io_sequence.h +++ b/src/test/osd/ceph_test_rados_io_sequence/ceph_test_rados_io_sequence.h @@ -1,5 +1,7 @@ +#include #include #include +#include #include #include "ProgramOptionReader.h" @@ -7,16 +9,12 @@ #include "common/io_exerciser/IoSequence.h" #include "common/io_exerciser/Model.h" #include "common/split.h" +#include "erasure-code/ErasureCodePlugin.h" #include "global/global_context.h" #include "global/global_init.h" #include "include/random.h" #include "librados/librados_asio.h" -#include -#include - -#include - /* Overview * * class SelectObjectSize @@ -37,10 +35,22 @@ * class SelectErasurePlugin * Selects an plugin for a test * + * class SelectErasurePacketSize + * Selects a packetsize to be used by jerasure + * + * class SelectErasureC + * Potentially selects a C value to be used for the shec plugin + * + * class SelectErasureW + * Potentially selects a W value to be used for the jerasure plugin + * * class SelectErasurePool * Selects an EC pool (plugin,k and m) for a test. Also creates the * pool as well. * + * class SelectErasureProfile + * Selects an EC profile for a test. Will create one if no name is specified + * * class SelectSeqRange * Selects a sequence or range of sequences for a test * @@ -69,10 +79,11 @@ namespace po = boost::program_options; namespace ceph { +class ErasureCodePlugin; + namespace io_sequence { namespace tester { // Choices for min and max object size -// Choices for min and max object size inline static constexpr size_t objectSizeSize = 10; inline static constexpr std::array, objectSizeSize> objectSizeChoices = {{{1, 32}, // Default - best for boundary checking @@ -113,54 +124,248 @@ using SelectNumThreads = io_sequence::tester ::threadArraySize, io_sequence::tester ::threadCountChoices>; -// Choices for EC k+m profile -inline constexpr int kmSize = 6; -inline constexpr std::array, kmSize> kmChoices = { - {{2, 2}, // Default - reasonable coverage - {2, 1}, - {2, 3}, - {3, 2}, - {4, 2}, - {5, 1}}}; - -using SelectErasureKM = - ProgramOptionSelector, - io_sequence::tester ::kmSize, - io_sequence::tester::kmChoices>; - -// Choices for EC chunk size -inline static constexpr int chunkSizeSize = 3; -inline static constexpr std::array chunkSizeChoices = { - {4 * 1024, 64 * 1024, 256 * 1024}}; - -using SelectErasureChunkSize = - ProgramOptionSelector; +class SelectSeqRange + : public ProgramOptionReader> { + public: + SelectSeqRange(po::variables_map& vm); + const std::pair + select() override; +}; // Choices for plugin -inline static constexpr int pluginListSize = 2; +inline static constexpr int pluginListSize = 5; inline static constexpr std::array - pluginChoices = {{"jerasure", "isa"}}; + pluginChoices = {{"jerasure", "isa", "clay", "shec", "lrc"}}; using SelectErasurePlugin = ProgramOptionSelector; -class SelectSeqRange - : public ProgramOptionReader> { +class SelectErasureKM + : public ProgramOptionGeneratedSelector> { public: - SelectSeqRange(po::variables_map& vm); - const std::pair - select() override; + SelectErasureKM(ceph::util::random_number_generator& rng, + po::variables_map& vm, + std::string_view plugin, + const std::optional& technique, + bool first_use); + + const std::vector> generate_selections() override; + + private: + ceph::util::random_number_generator& rng; + + std::string_view plugin; + std::optional technique; +}; + +namespace shec { +class SelectErasureC : public ProgramOptionGeneratedSelector { + public: + SelectErasureC(ceph::util::random_number_generator& rng, + po::variables_map& vm, + std::string_view plugin, + const std::optional>& km, + bool first_use); + + const std::vector generate_selections() override; + + private: + ceph::util::random_number_generator& rng; + + std::string_view plugin; + std::optional> km; +}; +} // namespace shec + +namespace jerasure { +class SelectErasureW : public ProgramOptionGeneratedSelector { + public: + SelectErasureW(ceph::util::random_number_generator& rng, + po::variables_map& vm, + std::string_view plugin, + const std::optional& technique, + const std::optional>& km, + const std::optional& packetsize, + bool first_use); + + const std::vector generate_selections() override; + + private: + ceph::util::random_number_generator& rng; + + std::string_view plugin; + std::optional technique; + std::optional> km; + std::optional packetsize; +}; + +class SelectErasurePacketSize + : public ProgramOptionGeneratedSelector { + public: + SelectErasurePacketSize(ceph::util::random_number_generator& rng, + po::variables_map& vm, + std::string_view plugin, + const std::optional& technique, + const std::optional>& km, + bool first_use); + + const std::vector generate_selections() override; + + private: + ceph::util::random_number_generator& rng; + + std::string_view plugin; + std::optional technique; + std::optional> km; +}; +} // namespace jerasure + +namespace lrc { +// Choices for lrc mappings and layers. The index selected for the mapping +// matches what index will be chosen from the layers array. +inline static constexpr int mappingLayerListSizes = 15; + +inline static std::array mappingChoices = {{ + "_DD", + "_DDD", + "_DDDD", + "_DDDDD", + "_DDDDDD", + "_D_D", + "_D_DD", + "_D_DDD", + "_D_DDDD", + "_D_DDDDD", + "_D_D_", + "_D_D_D", + "_D_D_DD", + "_D_D_DDD", + "_D_D_DDDD", +}}; + +inline static std::array layerChoices = {{ + "[[\"cDD\",\"\"]]", + "[[\"cDDD\",\"\"]]", + "[[\"cDDDD\",\"\"]]", + "[[\"cDDDDD\",\"\"]]", + "[[\"cDDDDDD\",\"\"]]", + "[[\"cDcD\",\"\"]]", + "[[\"cDcDD\",\"\"]]", + "[[\"cDcDDD\",\"\"]]", + "[[\"cDcDDDD\",\"\"]]", + "[[\"cDcDDDDD\",\"\"]]", + "[[\"cDcDc\",\"\"]]", + "[[\"cDcDcD\",\"\"]]", + "[[\"cDcDcDD\",\"\"]]", + "[[\"cDcDcDDD\",\"\"]]", + "[[\"cDcDcDDDD\",\"\"]]", +}}; + +using SelectMapping = + ProgramOptionSelector; + +using SelectLayers = + ProgramOptionSelector; + +class SelectMappingAndLayers { + public: + SelectMappingAndLayers(ceph::util::random_number_generator& rng, + po::variables_map& vm, + bool first_use); + const std::pair select(); + + private: + uint64_t rng_seed; + + ceph::util::random_number_generator mapping_rng; + ceph::util::random_number_generator layers_rng; + + SelectMapping sma; + SelectLayers sly; +}; +} // namespace lrc + +class SelectErasureTechnique + : public ProgramOptionGeneratedSelector { + public: + SelectErasureTechnique(ceph::util::random_number_generator& rng, + po::variables_map& vm, + std::string_view plugin, + bool first_use); + + const std::vector generate_selections() override; + + private: + ceph::util::random_number_generator& rng; + + std::string_view plugin; +}; + +class SelectErasureChunkSize : public ProgramOptionGeneratedSelector { + public: + SelectErasureChunkSize(ceph::util::random_number_generator& rng, + po::variables_map& vm, + ErasureCodeInterfaceRef ec_impl, + bool first_use); + const std::vector generate_selections() override; + + private: + ceph::util::random_number_generator& rng; + + ErasureCodeInterfaceRef ec_impl; +}; + +struct Profile { + std::string name; + std::string_view plugin; + std::optional technique; + std::optional> km; + std::optional packet_size; + std::optional c; + std::optional w; + std::optional mapping; + std::optional layers; + std::optional chunk_size; + std::optional jerasure_per_chunk_alignment; +}; + +class SelectErasureProfile : public ProgramOptionReader { + public: + SelectErasureProfile(boost::intrusive_ptr cct, + ceph::util::random_number_generator& rng, + po::variables_map& vm, librados::Rados& rados, + bool dry_run, bool first_use); + const Profile select() override; + void create(const Profile& profile); + const Profile selectExistingProfile(const std::string& profile_name); + + private: + boost::intrusive_ptr cct; + librados::Rados& rados; + bool dry_run; + ceph::util::random_number_generator& rng; + po::variables_map& vm; + + bool first_use; + + SelectErasurePlugin spl; + lrc::SelectMappingAndLayers sml; + + std::unique_ptr erasureCode; }; class SelectErasurePool : public ProgramOptionReader { public: - SelectErasurePool(ceph::util::random_number_generator& rng, - po::variables_map vm, + SelectErasurePool(boost::intrusive_ptr cct, + ceph::util::random_number_generator& rng, + po::variables_map& vm, librados::Rados& rados, bool dry_run, bool allow_pool_autoscaling, @@ -169,35 +374,37 @@ class SelectErasurePool : public ProgramOptionReader { 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; } - bool get_allow_pool_deep_scrubbing() { return allow_pool_deep_scrubbing; } - bool get_allow_pool_scrubbing() { return allow_pool_scrubbing; } - int getChosenK() const { return k; } - int getChosenM() const { return m; } + std::string create(); + void configureServices(bool allow_pool_autoscaling, + bool allow_pool_balancer, + bool allow_pool_deep_scrubbing, + bool allow_pool_scrubbing, + bool test_recovery); + + inline bool get_allow_pool_autoscaling() { return allow_pool_autoscaling; } + inline bool get_allow_pool_balancer() { return allow_pool_balancer; } + inline bool get_allow_pool_deep_scrubbing() { + return allow_pool_deep_scrubbing; + } + inline bool get_allow_pool_scrubbing() { return allow_pool_scrubbing; } + + inline std::optional getProfile() { return profile; } private: - 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; bool dry_run; + bool allow_pool_autoscaling; bool allow_pool_balancer; bool allow_pool_deep_scrubbing; bool allow_pool_scrubbing; bool test_recovery; - int k; - int m; - SelectErasureKM skm; - SelectErasurePlugin spl; - SelectErasureChunkSize scs; + bool first_use; + + SelectErasureProfile sep; + + std::optional profile; }; class TestObject { @@ -235,14 +442,17 @@ class TestObject { ceph::util::random_number_generator& rng; bool verbose; std::optional seqseed; - int poolK; - int poolM; + std::optional> poolKM; + std::optional> + poolMappingLayers; bool testrecovery; }; class TestRunner { public: - TestRunner(po::variables_map& vm, librados::Rados& rados); + TestRunner(boost::intrusive_ptr cct, + po::variables_map& vm, + librados::Rados& rados); ~TestRunner(); bool run_test();