#include "EcIoSequence.h"
+#include <algorithm>
#include <memory>
using IoOp = ceph::io_exerciser::IoOp;
bool EcIoSequence::is_supported(Sequence sequence) const { return true; }
std::unique_ptr<IoSequence> EcIoSequence::generate_sequence(
- Sequence sequence, std::pair<int, int> obj_size_range, int k, int m,
+ Sequence sequence, std::pair<int, int> obj_size_range,
+ std::optional<std::pair<int, int>> km,
+ std::optional<std::pair<std::string_view, std::string_view>> mappingLayers,
int seed) {
switch (sequence) {
case Sequence::SEQUENCE_SEQ0:
[[fallthrough]];
case Sequence::SEQUENCE_SEQ14:
return std::make_unique<ReadInjectSequence>(obj_size_range, seed,
- sequence, k, m);
+ sequence, km, mappingLayers);
case Sequence::SEQUENCE_SEQ10:
- return std::make_unique<Seq10>(obj_size_range, seed, k, m);
+ return std::make_unique<Seq10>(obj_size_range, seed, km, mappingLayers);
default:
ceph_abort_msg("Unrecognised sequence");
}
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<std::pair<int, int>> km,
+ std::optional<std::pair<std::string_view, std::string_view>>
+ 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<std::pair<int, int>> km,
+ std::optional<std::pair<std::string_view, std::string_view>>
+ 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<std::pair<int, int>> km,
+ std::optional<std::pair<std::string_view, std::string_view>>
+ 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<std::pair<int, int>> km,
+ std::optional<std::pair<std::string_view, std::string_view>>
+ 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;
}
}
ceph::io_exerciser::ReadInjectSequence::ReadInjectSequence(
- std::pair<int, int> obj_size_range, int seed, Sequence s, int k, int m)
+ std::pair<int, int> obj_size_range,
+ int seed,
+ Sequence s,
+ std::optional<std::pair<int, int>> km,
+ std::optional<std::pair<std::string_view, std::string_view>> 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();
}
}
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<IoOp> 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:
return DoneOp::generate();
}
-ceph::io_exerciser::Seq10::Seq10(std::pair<int, int> obj_size_range, int seed,
- int k, int m)
+ceph::io_exerciser::Seq10::Seq10(
+ std::pair<int, int> obj_size_range, int seed,
+ std::optional<std::pair<int, int>> km,
+ std::optional<std::pair<std::string_view, std::string_view>> mappingLayers)
: EcIoSequence(obj_size_range, seed),
offset(0),
length(1),
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) {
#include <boost/asio/io_context.hpp>
#include <iostream>
+#include <map>
#include <vector>
#include "common/Formatter.h"
#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"
#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"
"seed", po::value<int>(), "seed for whole test")(
"seqseed", po::value<int>(), "seed for sequence")(
"blocksize,b", po::value<Size>(), "block size (default 2048)")(
- "chunksize,c", po::value<Size>(), "chunk size (default 4096)")(
- "pool,p", po::value<std::string>(), "pool name")(
+ "pool,p", po::value<std::string>(), "existing pool name")(
+ "profile", po::value<std::string>(), "existing profile name")(
"object,o", po::value<std::string>()->default_value("test"),
- "object name")("km", po::value<Pair>(),
- "k,m EC pool profile (default 2,2)")(
- "plugin", po::value<PluginString>(), "EC plugin (isa or jerasure)")(
+ "object name")("plugin", po::value<PluginString>(), "EC plugin")(
+ "chunksize,c", po::value<Size>(), "chunk size (default 4096)")(
+ "km", po::value<Pair>(), "k,m EC pool profile (default 2,2)")(
+ "technique", po::value<std::string>(), "EC profile technique")(
+ "packetsize", po::value<uint64_t>(), "Jerasure EC profile packetsize")(
+ "w", po::value<uint64_t>(), "Jerasure EC profile w value")(
+ "c", po::value<uint64_t>(), "Shec EC profile c value")(
+ "mapping", po::value<std::string>(), "LRC EC profile mapping")(
+ "layers", po::value<std::string>(), "LRC EC profile layers")(
"objectsize", po::value<Pair>(),
"min,max object size in blocks (default 1,32)")(
"threads,t", po::value<int>(),
}
}
+ceph::io_sequence::tester::SelectErasureTechnique::SelectErasureTechnique(
+ ceph::util::random_number_generator<int>& rng,
+ po::variables_map& vm,
+ std::string_view plugin,
+ bool first_use)
+ : ProgramOptionGeneratedSelector<std::string>(rng, vm, "technique",
+ first_use),
+ rng(rng),
+ plugin(plugin) {}
+
+const std::vector<std::string>
+ceph::io_sequence::tester::SelectErasureTechnique::generate_selections() {
+ std::vector<std::string> 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<int>& 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<std::string, std::string>
+ceph::io_sequence::tester::lrc::SelectMappingAndLayers::select() {
+ return std::pair<std::string, std::string>(sma.select(), sly.select());
+}
+
+ceph::io_sequence::tester::SelectErasureKM::SelectErasureKM(
+ ceph::util::random_number_generator<int>& rng,
+ po::variables_map& vm,
+ std::string_view plugin,
+ const std::optional<std::string>& technique,
+ bool first_use)
+ : ProgramOptionGeneratedSelector<std::pair<int, int>>(rng, vm, "km",
+ first_use),
+ rng(rng),
+ plugin(plugin),
+ technique(technique) {}
+
+const std::vector<std::pair<int, int>>
+ceph::io_sequence::tester::SelectErasureKM::generate_selections() {
+ std::vector<std::pair<int, int>> 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<int>& rng,
+ po::variables_map& vm,
+ std::string_view plugin,
+ const std::optional<std::string_view>& technique,
+ const std::optional<std::pair<int, int>>& km,
+ const std::optional<uint64_t>& packetsize,
+ bool first_use)
+ : ProgramOptionGeneratedSelector<uint64_t>(rng, vm, "w", first_use),
+ rng(rng),
+ plugin(plugin),
+ km(km),
+ packetsize(packetsize) {}
+
+const std::vector<uint64_t>
+ceph::io_sequence::tester::jerasure::SelectErasureW::generate_selections() {
+ std::vector<uint64_t> 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<int>& rng,
+ po::variables_map& vm,
+ std::string_view plugin,
+ const std::optional<std::pair<int, int>>& km,
+ bool first_use)
+ : ProgramOptionGeneratedSelector<uint64_t>(rng, vm, "c", first_use),
+ rng(rng),
+ plugin(plugin),
+ km(km) {}
+
+const std::vector<uint64_t>
+ceph::io_sequence::tester::shec::SelectErasureC::generate_selections() {
+ if (plugin != "shec") {
+ return {};
+ }
+
+ std::vector<uint64_t> 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<int>& rng,
+ po::variables_map& vm,
+ std::string_view plugin,
+ const std::optional<std::string_view>& technique,
+ const std::optional<std::pair<int, int>>& km,
+ bool first_use)
+ : ProgramOptionGeneratedSelector<uint64_t>(rng, vm, "packetsize",
+ first_use),
+ rng(rng),
+ plugin(plugin),
+ technique(technique),
+ km(km) {}
+
+const std::vector<uint64_t> ceph::io_sequence::tester::jerasure::
+ SelectErasurePacketSize::generate_selections() {
+ std::vector<uint64_t> 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<int>& 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<uint64_t>
+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<uint64_t> 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<CephContext> cct,
+ ceph::util::random_number_generator<int>& 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<std::string, 9> 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<std::string, std::string> 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<std::string>("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<JSONFormatter>(false);
+
+ std::vector<std::string> 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<JSONFormatter>(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<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)
+ boost::intrusive_ptr<CephContext> cct,
+ 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)
: ProgramOptionReader<std::string>(vm, "pool"),
rados(rados),
dry_run(dry_run),
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<std::string>();
+ first_use(true),
+ sep{cct, rng, vm, rados, dry_run, first_use} {
+ if (isForced()) {
+ std::array<std::string, 9> 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<int, int> value;
- if (!skm.isForced() && force_value.has_value()) {
- int rc;
- bufferlist inbl, outbl;
- auto formatter = std::make_unique<JSONFormatter>(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<JSONFormatter>(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<JSONFormatter>(false);
+ auto formatter = std::make_shared<JSONFormatter>(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<JSONFormatter>(false);
+
+ if (!allow_pool_autoscaling) {
ceph::messaging::osd::OSDSetRequest setNoAutoscaleRequest{"noautoscale",
std::nullopt};
rc = send_mon_command(setNoAutoscaleRequest, rados, "OSDSetRequest", inbl,
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);
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();
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()));
} 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()));
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<CephContext> cct,
+ po::variables_map& vm,
+ librados::Rados& rados)
: rados(rados),
seed(vm.contains("seed") ? vm["seed"].as<int>() : time(nullptr)),
rng(ceph::util::random_number_generator<int>(seed)),
sbs{rng, vm, "blocksize", true},
sos{rng, vm, "objectsize", true},
- spo{rng,
+ spo{cct,
+ rng,
vm,
rados,
vm.contains("dryrun"),
ceph::io_exerciser::Sequence s = ceph::io_exerciser::Sequence::SEQUENCE_BEGIN;
std::unique_ptr<ceph::io_exerciser::IoSequence> seq;
if (testrecovery) {
+ std::optional<ceph::io_sequence::tester::Profile> profile =
+ spo.getProfile();
+ std::optional<std::pair<int, int>> km;
+ std::optional<std::pair<std::string_view, std::string_view>> 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()));
}
std::optional<std::string>
-ceph::io_sequence::tester::TestRunner ::get_optional_token() {
+ceph::io_sequence::tester::TestRunner::get_optional_token() {
std::optional<std::string> ret = std::nullopt;
if (tokens != split.end()) {
ret = std::string(*tokens++);
}
std::optional<uint64_t>
-ceph::io_sequence::tester::TestRunner ::get_optional_numeric_token() {
+ceph::io_sequence::tester::TestRunner::get_optional_numeric_token() {
std::string parse_error;
std::optional<std::string> token = get_optional_token();
if (token) {
std::unique_ptr<ceph::io_sequence::tester::TestRunner> runner;
try {
- runner = std::make_unique<ceph::io_sequence::tester::TestRunner>(vm, rados);
+ runner =
+ std::make_unique<ceph::io_sequence::tester::TestRunner>(cct, vm, rados);
} catch (const po::error& e) {
return 1;
}
+#include <boost/asio/io_context.hpp>
#include <boost/program_options.hpp>
#include <optional>
+#include <string>
#include <utility>
#include "ProgramOptionReader.h"
#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 <boost/asio/io_context.hpp>
-#include <boost/program_options.hpp>
-
-#include <optional>
-
/* Overview
*
* class SelectObjectSize
* 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
*
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<std::pair<int, int>, objectSizeSize>
objectSizeChoices = {{{1, 32}, // Default - best for boundary checking
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 = {
- {{2, 2}, // Default - reasonable coverage
- {2, 1},
- {2, 3},
- {3, 2},
- {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 static constexpr int chunkSizeSize = 3;
-inline static constexpr std::array<uint64_t, chunkSizeSize> chunkSizeChoices = {
- {4 * 1024, 64 * 1024, 256 * 1024}};
-
-using SelectErasureChunkSize =
- ProgramOptionSelector<uint64_t,
- io_sequence::tester ::chunkSizeSize,
- io_sequence::tester::chunkSizeChoices>;
+class SelectSeqRange
+ : public ProgramOptionReader<std::pair<ceph::io_exerciser ::Sequence,
+ ceph::io_exerciser ::Sequence>> {
+ public:
+ SelectSeqRange(po::variables_map& vm);
+ const std::pair<ceph::io_exerciser::Sequence, ceph::io_exerciser::Sequence>
+ select() override;
+};
// Choices for plugin
-inline static constexpr int pluginListSize = 2;
+inline static constexpr int pluginListSize = 5;
inline static constexpr std::array<std::string_view, pluginListSize>
- pluginChoices = {{"jerasure", "isa"}};
+ pluginChoices = {{"jerasure", "isa", "clay", "shec", "lrc"}};
using SelectErasurePlugin =
ProgramOptionSelector<std::string_view,
io_sequence::tester ::pluginListSize,
io_sequence::tester ::pluginChoices>;
-class SelectSeqRange
- : public ProgramOptionReader<std::pair<ceph::io_exerciser ::Sequence,
- ceph::io_exerciser ::Sequence>> {
+class SelectErasureKM
+ : public ProgramOptionGeneratedSelector<std::pair<int, int>> {
public:
- SelectSeqRange(po::variables_map& vm);
- const std::pair<ceph::io_exerciser::Sequence, ceph::io_exerciser::Sequence>
- select() override;
+ SelectErasureKM(ceph::util::random_number_generator<int>& rng,
+ po::variables_map& vm,
+ std::string_view plugin,
+ const std::optional<std::string>& technique,
+ bool first_use);
+
+ const std::vector<std::pair<int, int>> generate_selections() override;
+
+ private:
+ ceph::util::random_number_generator<int>& rng;
+
+ std::string_view plugin;
+ std::optional<std::string> technique;
+};
+
+namespace shec {
+class SelectErasureC : public ProgramOptionGeneratedSelector<uint64_t> {
+ public:
+ SelectErasureC(ceph::util::random_number_generator<int>& rng,
+ po::variables_map& vm,
+ std::string_view plugin,
+ const std::optional<std::pair<int, int>>& km,
+ bool first_use);
+
+ const std::vector<uint64_t> generate_selections() override;
+
+ private:
+ ceph::util::random_number_generator<int>& rng;
+
+ std::string_view plugin;
+ std::optional<std::pair<int, int>> km;
+};
+} // namespace shec
+
+namespace jerasure {
+class SelectErasureW : public ProgramOptionGeneratedSelector<uint64_t> {
+ public:
+ SelectErasureW(ceph::util::random_number_generator<int>& rng,
+ po::variables_map& vm,
+ std::string_view plugin,
+ const std::optional<std::string_view>& technique,
+ const std::optional<std::pair<int, int>>& km,
+ const std::optional<uint64_t>& packetsize,
+ bool first_use);
+
+ const std::vector<uint64_t> generate_selections() override;
+
+ private:
+ ceph::util::random_number_generator<int>& rng;
+
+ std::string_view plugin;
+ std::optional<std::string_view> technique;
+ std::optional<std::pair<int, int>> km;
+ std::optional<uint64_t> packetsize;
+};
+
+class SelectErasurePacketSize
+ : public ProgramOptionGeneratedSelector<uint64_t> {
+ public:
+ SelectErasurePacketSize(ceph::util::random_number_generator<int>& rng,
+ po::variables_map& vm,
+ std::string_view plugin,
+ const std::optional<std::string_view>& technique,
+ const std::optional<std::pair<int, int>>& km,
+ bool first_use);
+
+ const std::vector<uint64_t> generate_selections() override;
+
+ private:
+ ceph::util::random_number_generator<int>& rng;
+
+ std::string_view plugin;
+ std::optional<std::string_view> technique;
+ std::optional<std::pair<int, int>> 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<std::string, mappingLayerListSizes> 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<std::string, mappingLayerListSizes> layerChoices = {{
+ "[[\"cDD\",\"\"]]",
+ "[[\"cDDD\",\"\"]]",
+ "[[\"cDDDD\",\"\"]]",
+ "[[\"cDDDDD\",\"\"]]",
+ "[[\"cDDDDDD\",\"\"]]",
+ "[[\"cDcD\",\"\"]]",
+ "[[\"cDcDD\",\"\"]]",
+ "[[\"cDcDDD\",\"\"]]",
+ "[[\"cDcDDDD\",\"\"]]",
+ "[[\"cDcDDDDD\",\"\"]]",
+ "[[\"cDcDc\",\"\"]]",
+ "[[\"cDcDcD\",\"\"]]",
+ "[[\"cDcDcDD\",\"\"]]",
+ "[[\"cDcDcDDD\",\"\"]]",
+ "[[\"cDcDcDDDD\",\"\"]]",
+}};
+
+using SelectMapping =
+ ProgramOptionSelector<std::string,
+ io_sequence::tester::lrc::mappingLayerListSizes,
+ io_sequence::tester::lrc::mappingChoices>;
+
+using SelectLayers =
+ ProgramOptionSelector<std::string,
+ io_sequence::tester::lrc::mappingLayerListSizes,
+ io_sequence::tester::lrc::layerChoices>;
+
+class SelectMappingAndLayers {
+ public:
+ SelectMappingAndLayers(ceph::util::random_number_generator<int>& rng,
+ po::variables_map& vm,
+ bool first_use);
+ const std::pair<std::string, std::string> select();
+
+ private:
+ uint64_t rng_seed;
+
+ ceph::util::random_number_generator<int> mapping_rng;
+ ceph::util::random_number_generator<int> layers_rng;
+
+ SelectMapping sma;
+ SelectLayers sly;
+};
+} // namespace lrc
+
+class SelectErasureTechnique
+ : public ProgramOptionGeneratedSelector<std::string> {
+ public:
+ SelectErasureTechnique(ceph::util::random_number_generator<int>& rng,
+ po::variables_map& vm,
+ std::string_view plugin,
+ bool first_use);
+
+ const std::vector<std::string> generate_selections() override;
+
+ private:
+ ceph::util::random_number_generator<int>& rng;
+
+ std::string_view plugin;
+};
+
+class SelectErasureChunkSize : public ProgramOptionGeneratedSelector<uint64_t> {
+ public:
+ SelectErasureChunkSize(ceph::util::random_number_generator<int>& rng,
+ po::variables_map& vm,
+ ErasureCodeInterfaceRef ec_impl,
+ bool first_use);
+ const std::vector<uint64_t> generate_selections() override;
+
+ private:
+ ceph::util::random_number_generator<int>& rng;
+
+ ErasureCodeInterfaceRef ec_impl;
+};
+
+struct Profile {
+ std::string name;
+ std::string_view plugin;
+ std::optional<std::string> technique;
+ std::optional<std::pair<int, int>> km;
+ std::optional<uint64_t> packet_size;
+ std::optional<int> c;
+ std::optional<int> w;
+ std::optional<std::string> mapping;
+ std::optional<std::string> layers;
+ std::optional<uint64_t> chunk_size;
+ std::optional<bool> jerasure_per_chunk_alignment;
+};
+
+class SelectErasureProfile : public ProgramOptionReader<Profile> {
+ public:
+ SelectErasureProfile(boost::intrusive_ptr<CephContext> cct,
+ ceph::util::random_number_generator<int>& 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<CephContext> cct;
+ librados::Rados& rados;
+ bool dry_run;
+ ceph::util::random_number_generator<int>& rng;
+ po::variables_map& vm;
+
+ bool first_use;
+
+ SelectErasurePlugin spl;
+ lrc::SelectMappingAndLayers sml;
+
+ std::unique_ptr<ErasureCodePlugin> erasureCode;
};
class SelectErasurePool : public ProgramOptionReader<std::string> {
public:
- SelectErasurePool(ceph::util::random_number_generator<int>& rng,
- po::variables_map vm,
+ SelectErasurePool(boost::intrusive_ptr<CephContext> cct,
+ ceph::util::random_number_generator<int>& rng,
+ po::variables_map& vm,
librados::Rados& rados,
bool dry_run,
bool allow_pool_autoscaling,
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<Profile> 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> profile;
};
class TestObject {
ceph::util::random_number_generator<int>& rng;
bool verbose;
std::optional<int> seqseed;
- int poolK;
- int poolM;
+ std::optional<std::pair<int, int>> poolKM;
+ std::optional<std::pair<std::string_view, std::string_view>>
+ poolMappingLayers;
bool testrecovery;
};
class TestRunner {
public:
- TestRunner(po::variables_map& vm, librados::Rados& rados);
+ TestRunner(boost::intrusive_ptr<CephContext> cct,
+ po::variables_map& vm,
+ librados::Rados& rados);
~TestRunner();
bool run_test();