Add sequences to test IOs with simple error injects, along with some small fixes for previous error inject implementation.
Signed-off-by: Jon Bailey <jonathan.bailey1@ibm.com>
ObjectModel.cc
RadosIo.cc
JsonStructures.cc
+ EcIoSequence.cc
)
target_link_libraries(object_io_exerciser
: DataGenerator(model) {}
virtual bufferptr generate_block(uint64_t offset);
- virtual bufferlist generate_data(uint64_t length, uint64_t offset);
+ virtual bufferlist generate_data(uint64_t length, uint64_t offset) override;
virtual bufferptr generate_wrong_block(uint64_t offset);
virtual bufferlist generate_wrong_data(uint64_t offset, uint64_t length) override;
};
--- /dev/null
+#include "EcIoSequence.h"
+
+#include <memory>
+
+using IoOp = ceph::io_exerciser::IoOp;
+using Sequence = ceph::io_exerciser::Sequence;
+using IoSequence = ceph::io_exerciser::IoSequence;
+using EcIoSequence = ceph::io_exerciser::EcIoSequence;
+using ReadInjectSequence = ceph::io_exerciser::ReadInjectSequence;
+
+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,
+ int seed)
+{
+ switch(sequence)
+ {
+ case Sequence::SEQUENCE_SEQ0:
+ [[ fallthrough ]];
+ case Sequence::SEQUENCE_SEQ1:
+ [[ fallthrough ]];
+ case Sequence::SEQUENCE_SEQ2:
+ [[ fallthrough ]];
+ case Sequence::SEQUENCE_SEQ3:
+ [[ fallthrough ]];
+ case Sequence::SEQUENCE_SEQ4:
+ [[ fallthrough ]];
+ case Sequence::SEQUENCE_SEQ5:
+ [[ fallthrough ]];
+ case Sequence::SEQUENCE_SEQ6:
+ [[ fallthrough ]];
+ case Sequence::SEQUENCE_SEQ7:
+ [[ fallthrough ]];
+ case Sequence::SEQUENCE_SEQ8:
+ [[ fallthrough ]];
+ case Sequence::SEQUENCE_SEQ9:
+ return std::make_unique<ReadInjectSequence>(obj_size_range, seed,
+ sequence, k, m);
+ case Sequence::SEQUENCE_SEQ10:
+ return std::make_unique<Seq10>(obj_size_range, seed, k, m);
+ default:
+ ceph_abort_msg("Unrecognised sequence");
+ }
+}
+
+EcIoSequence::EcIoSequence(std::pair<int,int> obj_size_range, int seed) :
+ IoSequence(obj_size_range, seed),
+ setup_inject(false), 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);
+ setup_inject = true;
+}
+
+void EcIoSequence::select_random_data_shard_to_inject_write_error(int k, int m)
+{
+ // Write errors do not support injecting to the primary OSD
+ shard_to_inject = rng(1, k - 1);
+ setup_inject = true;
+}
+
+void EcIoSequence::select_random_shard_to_inject_read_error(int k, int m)
+{
+ shard_to_inject = rng(k + m - 1);
+ setup_inject = true;
+}
+
+void EcIoSequence::select_random_shard_to_inject_write_error(int k, int m)
+{
+ // Write errors do not support injecting to the primary OSD
+ shard_to_inject = rng(1, k + m - 1);
+ setup_inject = true;
+}
+
+void EcIoSequence::generate_random_read_inject_type()
+{
+ inject_op_type = static_cast<InjectOpType>(rng(static_cast<int>(InjectOpType::ReadEIO),
+ static_cast<int>(InjectOpType::ReadMissingShard)));
+}
+
+void EcIoSequence::generate_random_write_inject_type()
+{
+ inject_op_type = static_cast<InjectOpType>(rng(static_cast<int>(InjectOpType::WriteFailAndRollback),
+ static_cast<int>(InjectOpType::WriteOSDAbort)));
+}
+
+ceph::io_exerciser::ReadInjectSequence::ReadInjectSequence(std::pair<int,int> obj_size_range,
+ int seed,
+ Sequence s,
+ int k, int m) :
+ 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);
+ generate_random_read_inject_type();
+}
+
+Sequence ceph::io_exerciser::ReadInjectSequence::get_id() const
+{
+ return child_sequence->get_id();
+}
+
+std::string ceph::io_exerciser::ReadInjectSequence::get_name() const
+{
+ return child_sequence->get_name() +
+ " running with read errors injected on shard "
+ + std::to_string(*shard_to_inject);
+}
+
+std::unique_ptr<IoOp> ReadInjectSequence::next()
+{
+ step++;
+
+ if (nextOp)
+ {
+ std::unique_ptr<IoOp> retOp = nullptr;
+ nextOp.swap(retOp);
+ return retOp;
+ }
+
+ std::unique_ptr<IoOp> childOp = child_sequence->next();
+
+ switch(childOp->getOpType())
+ {
+ case OpType::Remove:
+ nextOp.swap(childOp);
+ switch(inject_op_type)
+ {
+ case InjectOpType::ReadEIO:
+ return ClearReadErrorInjectOp::generate(*shard_to_inject, 0);
+ case InjectOpType::ReadMissingShard:
+ return ClearReadErrorInjectOp::generate(*shard_to_inject, 1);
+ case InjectOpType::WriteFailAndRollback:
+ return ClearWriteErrorInjectOp::generate(*shard_to_inject, 0);
+ case InjectOpType::WriteOSDAbort:
+ return ClearWriteErrorInjectOp::generate(*shard_to_inject, 3);
+ case InjectOpType::None:
+ [[ fallthrough ]];
+ default:
+ ceph_abort_msg("Unsupported operation");
+ }
+ break;
+ case OpType::Create:
+ switch(inject_op_type)
+ {
+ case InjectOpType::ReadEIO:
+ nextOp = InjectReadErrorOp::generate(*shard_to_inject, 0, 0, std::numeric_limits<uint64_t>::max());
+ break;
+ case InjectOpType::ReadMissingShard:
+ nextOp = InjectReadErrorOp::generate(*shard_to_inject, 1, 0, std::numeric_limits<uint64_t>::max());
+ break;
+ case InjectOpType::WriteFailAndRollback:
+ nextOp = InjectWriteErrorOp::generate(*shard_to_inject, 0, 0, std::numeric_limits<uint64_t>::max());
+ break;
+ case InjectOpType::WriteOSDAbort:
+ nextOp = InjectWriteErrorOp::generate(*shard_to_inject, 3, 0, std::numeric_limits<uint64_t>::max());
+ break;
+ case InjectOpType::None:
+ [[ fallthrough ]];
+ default:
+ ceph_abort_msg("Unsupported operation");
+ }
+ break;
+ default:
+ // Do nothing in default case
+ break;
+ }
+
+ return childOp;
+}
+
+std::unique_ptr<ceph::io_exerciser::IoOp> ceph::io_exerciser::ReadInjectSequence::_next()
+{
+ ceph_abort_msg("Should not reach this point, "
+ "this sequence should only consume complete sequences");
+
+ return DoneOp::generate();
+}
+
+
+
+ceph::io_exerciser::Seq10::Seq10(std::pair<int,int> obj_size_range, int seed,
+ int k, int m) :
+ EcIoSequence(obj_size_range, seed), offset(0), length(1),
+ failed_write_done(false), read_done(false), successful_write_done(false),
+ test_all_lengths(false), // Only test length(1) due to time constraints
+ test_all_sizes(false) // Only test obj_size(rand()) due to time constraints
+{
+ select_random_shard_to_inject_write_error(k, m);
+ // We will inject specifically as part of our sequence in this sequence
+ setup_inject = false;
+ if (!test_all_sizes)
+ {
+ select_random_object_size();
+ }
+}
+
+Sequence ceph::io_exerciser::Seq10::get_id() const
+{
+ return Sequence::SEQUENCE_SEQ10;
+}
+
+std::string ceph::io_exerciser::Seq10::get_name() const
+{
+ return "Sequential writes of length " + std::to_string(length) +
+ " with queue depth 1"
+ " first injecting a failed write and read it to ensure it rolls back, then"
+ " successfully writing the data and reading the write the ensure it is applied";
+}
+
+std::unique_ptr<ceph::io_exerciser::IoOp> ceph::io_exerciser::Seq10::_next()
+{
+ if (!inject_error_done)
+ {
+ inject_error_done = true;
+ return InjectWriteErrorOp::generate(*shard_to_inject, 0, 0,
+ std::numeric_limits<uint64_t>::max());
+ }
+ else if (!failed_write_done)
+ {
+ failed_write_done = true;
+ read_done = false;
+ barrier = true;
+ return SingleFailedWriteOp::generate(offset, length);
+ }
+ else if (failed_write_done && !read_done)
+ {
+ read_done = true;
+ barrier = true;
+ return SingleReadOp::generate(offset, length);
+ }
+ else if (!clear_inject_done)
+ {
+ clear_inject_done = true;
+ return ClearWriteErrorInjectOp::generate(*shard_to_inject, 0);
+ }
+ else if (!successful_write_done)
+ {
+ successful_write_done = true;
+ read_done = false;
+ barrier = true;
+ return SingleWriteOp::generate(offset, length);
+ }
+ else if (successful_write_done && !read_done)
+ {
+ read_done = true;
+ return SingleReadOp::generate(offset, length);
+ }
+ else if (successful_write_done && read_done)
+ {
+ offset++;
+ inject_error_done = false;
+ failed_write_done = false;
+ read_done = false;
+ clear_inject_done = false;
+ successful_write_done = false;
+
+ if (offset + length >= obj_size) {
+ if (!test_all_lengths)
+ {
+ done = true;
+ return BarrierOp::generate();
+ }
+
+ offset = 0;
+ length++;
+ if (length > obj_size) {
+ if (!test_all_sizes)
+ {
+ done = true;
+ return BarrierOp::generate();
+ }
+
+ length = 1;
+ return increment_object_size();
+ }
+ }
+
+ return BarrierOp::generate();
+ }
+ else
+ {
+ ceph_abort_msg("Sequence in undefined state. Aborting");
+ return DoneOp::generate();
+ }
+}
\ No newline at end of file
--- /dev/null
+#include "IoSequence.h"
+
+namespace ceph
+{
+ namespace io_exerciser
+ {
+ class EcIoSequence : public IoSequence
+ {
+ public:
+ virtual bool is_supported(Sequence sequence) const override;
+ static std::unique_ptr<IoSequence>
+ generate_sequence(Sequence s,
+ std::pair<int,int> obj_size_range,
+ int k,
+ int m,
+ int seed );
+
+ protected:
+ bool setup_inject;
+ bool clear_inject;
+ std::optional<uint64_t> shard_to_inject;
+ InjectOpType inject_op_type;
+
+ EcIoSequence(std::pair<int,int> obj_size_range, int seed);
+
+ // 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 generate_random_read_inject_type();
+ void generate_random_write_inject_type();
+ };
+
+ class ReadInjectSequence : public EcIoSequence
+ {
+ public:
+ ReadInjectSequence(std::pair<int,int> obj_size_range, int seed, Sequence s, int k, int m);
+
+ Sequence get_id() const override;
+ std::string get_name() const override;
+ virtual std::unique_ptr<IoOp> next() override;
+ std::unique_ptr<IoOp> _next() override;
+
+ private:
+ std::unique_ptr<IoSequence> child_sequence;
+ std::unique_ptr<IoOp> nextOp;
+ };
+
+ class Seq10: public EcIoSequence {
+ public:
+ Seq10(std::pair<int,int> obj_size_range, int seed, int k, int m);
+
+ Sequence get_id() const override;
+ std::string get_name() const override;
+ std::unique_ptr<IoOp> _next() override;
+
+ private:
+ uint64_t offset;
+ uint64_t length;
+
+ bool inject_error_done;
+ bool failed_write_done;
+ bool read_done;
+ bool clear_inject_done;
+ bool successful_write_done;
+ bool test_all_lengths;
+ bool test_all_sizes;
+ };
+ }
+}
\ No newline at end of file
case Sequence::SEQUENCE_SEQ9:
os << "SEQUENCE_SEQ9";
break;
+ case Sequence::SEQUENCE_SEQ10:
+ os << "SEQUENCE_SEQ10";
+ break;
case Sequence::SEQUENCE_END:
os << "SEQUENCE_END";
break;
return os;
}
+bool IoSequence::is_supported(Sequence sequence) const
+{
+ return sequence != Sequence::SEQUENCE_SEQ10;
+}
+
std::unique_ptr<IoSequence> IoSequence::generate_sequence(Sequence s,
std::pair<int,int> obj_size_range,
int seed)
return std::make_unique<Seq8>(obj_size_range, seed);
case Sequence::SEQUENCE_SEQ9:
return std::make_unique<Seq9>(obj_size_range, seed);
+ case Sequence::SEQUENCE_SEQ10:
+ ceph_abort_msg("Sequence 10 only supported for erasure coded pools "
+ "through the EcIoSequence interface");
+ return nullptr;
default:
break;
}
}
IoSequence::IoSequence(std::pair<int,int> obj_size_range,
- int seed) :
+ int seed) :
min_obj_size(obj_size_range.first), max_obj_size(obj_size_range.second),
create(true), barrier(false), done(false), remove(false),
obj_size(min_obj_size), step(-1), seed(seed)
rng.seed(seed);
}
+std::string ceph::io_exerciser::IoSequence::get_name_with_seqseed() const
+{
+ return get_name() + " (seqseed " + std::to_string(get_seed()) + ")";
+}
+
int IoSequence::get_step() const
{
return step;
return BarrierOp::generate();
}
+Sequence IoSequence::getNextSupportedSequenceId() const
+{
+ Sequence sequence = get_id();
+ ++sequence;
+ for (;sequence < Sequence::SEQUENCE_END;
+ ++sequence)
+ {
+ if (is_supported(sequence))
+ {
+ return sequence;
+ }
+ }
+
+ return Sequence::SEQUENCE_END;
+}
+
std::unique_ptr<IoOp> IoSequence::next()
{
step++;
length = 1 + rng(obj_size - 1);
}
+Sequence ceph::io_exerciser::Seq0::get_id() const
+{
+ return Sequence::SEQUENCE_SEQ0;
+}
+
std::string ceph::io_exerciser::Seq0::get_name() const
{
return "Sequential reads of length " + std::to_string(length) +
- " with queue depth 1 (seqseed " + std::to_string(get_seed()) + ")";
+ " with queue depth 1";
}
std::unique_ptr<ceph::io_exerciser::IoOp> ceph::io_exerciser::Seq0::_next()
count = 3 * obj_size;
}
+Sequence ceph::io_exerciser::Seq1::get_id() const
+{
+ return Sequence::SEQUENCE_SEQ1;
+}
+
std::string ceph::io_exerciser::Seq1::get_name() const
{
- return "Random offset, random length read/write I/O with queue depth 1 (seqseed "
- + std::to_string(get_seed()) + ")";
+ return "Random offset, random length read/write I/O with queue depth 1";
}
std::unique_ptr<ceph::io_exerciser::IoOp> ceph::io_exerciser::Seq1::_next()
ceph::io_exerciser::Seq2::Seq2(std::pair<int,int> obj_size_range, int seed) :
- IoSequence(obj_size_range, seed), offset(0), length(0) {}
+ IoSequence(obj_size_range, seed), offset(0), length(0)
+{
+
+}
+
+Sequence ceph::io_exerciser::Seq2::get_id() const
+{
+ return Sequence::SEQUENCE_SEQ2;
+}
std::string ceph::io_exerciser::Seq2::get_name() const
{
set_min_object_size(2);
}
+Sequence ceph::io_exerciser::Seq3::get_id() const
+{
+ return Sequence::SEQUENCE_SEQ3;
+}
+
std::string ceph::io_exerciser::Seq3::get_name() const
{
return "Permutations of offset 2-region 1-block read I/O";
set_min_object_size(3);
}
+Sequence ceph::io_exerciser::Seq4::get_id() const
+{
+ return Sequence::SEQUENCE_SEQ4;
+}
+
std::string ceph::io_exerciser::Seq4::get_name() const
{
return "Permutations of offset 3-region 1-block read I/O";
ceph::io_exerciser::Seq5::Seq5(std::pair<int,int> obj_size_range, int seed) :
IoSequence(obj_size_range, seed), offset(0), length(1),
- doneread(false), donebarrier(false) {}
+ doneread(false), donebarrier(false)
+{
+
+}
+
+Sequence ceph::io_exerciser::Seq5::get_id() const
+{
+ return Sequence::SEQUENCE_SEQ5;
+}
std::string ceph::io_exerciser::Seq5::get_name() const
{
ceph::io_exerciser::Seq6::Seq6(std::pair<int,int> obj_size_range, int seed) :
IoSequence(obj_size_range, seed), offset(0), length(1),
- doneread(false), donebarrier(false) {}
+ doneread(false), donebarrier(false)
+{
+
+}
+
+Sequence ceph::io_exerciser::Seq6::get_id() const
+{
+ return Sequence::SEQUENCE_SEQ6;
+}
std::string ceph::io_exerciser::Seq6::get_name() const
{
offset = obj_size;
}
+Sequence ceph::io_exerciser::Seq7::get_id() const
+{
+ return Sequence::SEQUENCE_SEQ7;
+}
+
std::string ceph::io_exerciser::Seq7::get_name() const
{
return "Permutations of offset 2-region 1-block writes";
set_min_object_size(3);
}
+Sequence ceph::io_exerciser::Seq8::get_id() const
+{
+ return Sequence::SEQUENCE_SEQ8;
+}
+
std::string ceph::io_exerciser::Seq8::get_name() const
{
return "Permutations of offset 3-region 1-block write I/O";
}
+Sequence ceph::io_exerciser::Seq9::get_id() const
+{
+ return Sequence::SEQUENCE_SEQ9;
+}
+
std::string ceph::io_exerciser::Seq9::get_name() const
{
return "Permutations of offset and length write I/O";
SEQUENCE_SEQ7,
SEQUENCE_SEQ8,
SEQUENCE_SEQ9,
- //
+ SEQUENCE_SEQ10,
+
SEQUENCE_END,
SEQUENCE_BEGIN = SEQUENCE_SEQ0
};
public:
virtual ~IoSequence() = default;
+ virtual Sequence get_id() const = 0;
+ virtual std::string get_name_with_seqseed() const;
virtual std::string get_name() const = 0;
int get_step() const;
int get_seed() const;
+ virtual Sequence getNextSupportedSequenceId() const;
virtual std::unique_ptr<IoOp> next();
+ virtual bool is_supported(Sequence sequence) const;
static std::unique_ptr<IoSequence>
generate_sequence(Sequence s,
std::pair<int,int> obj_size_range,
public:
Seq0(std::pair<int,int> obj_size_range, int seed);
+ Sequence get_id() const override;
std::string get_name() const override;
std::unique_ptr<IoOp> _next() override;
public:
Seq1(std::pair<int,int> obj_size_range, int seed);
+ Sequence get_id() const override;
std::string get_name() const override;
- std::unique_ptr<IoOp> _next();
+ std::unique_ptr<IoOp> _next() override;
private:
int count;
public:
Seq2(std::pair<int,int> obj_size_range, int seed);
+ Sequence get_id() const override;
std::string get_name() const override;
std::unique_ptr<IoOp> _next() override;
public:
Seq3(std::pair<int,int> obj_size_range, int seed);
+ Sequence get_id() const override;
std::string get_name() const override;
std::unique_ptr<IoOp> _next() override;
private:
public:
Seq4(std::pair<int,int> obj_size_range, int seed);
+ Sequence get_id() const override;
std::string get_name() const override;
std::unique_ptr<IoOp> _next() override;
public:
Seq5(std::pair<int,int> obj_size_range, int seed);
+ Sequence get_id() const override;
std::string get_name() const override;
std::unique_ptr<IoOp> _next() override;
public:
Seq6(std::pair<int,int> obj_size_range, int seed);
+ Sequence get_id() const override;
std::string get_name() const override;
std::unique_ptr<IoOp> _next() override;
public:
Seq7(std::pair<int,int> obj_size_range, int seed);
+ Sequence get_id() const override;
std::string get_name() const override;
std::unique_ptr<IoOp> _next() override;
public:
Seq8(std::pair<int,int> obj_size_range, int seed);
+ Sequence get_id() const override;
std::string get_name() const override;
std::unique_ptr<IoOp> _next() override;
private:
public:
Seq9(std::pair<int,int> obj_size_range, int seed);
+ Sequence get_id() const override;
std::string get_name() const override;
-
std::unique_ptr<IoOp> _next() override;
};
}
#include "JsonStructures.h"
#include "common/ceph_json.h"
+#include "OpType.h"
using namespace ceph::io_exerciser::json;
formatter->close_section();
}
-OSDECProfileSetRequest::OSDECProfileSetRequest(const std::string& name,
- std::vector<std::string> profile,
- std::shared_ptr<ceph::Formatter> formatter) :
+ceph::io_exerciser::json::OSDPoolGetRequest
+ ::OSDPoolGetRequest(const std::string& pool_name,
+ std::shared_ptr<ceph::Formatter> formatter) :
+ JSONStructure(formatter),
+ pool(pool_name)
+{
+
+}
+
+ceph::io_exerciser::json::OSDPoolGetRequest
+ ::OSDPoolGetRequest(JSONObj* obj,
+ std::shared_ptr<ceph::Formatter> formatter) :
+ JSONStructure(formatter)
+{
+ ceph::io_exerciser::json::OSDPoolGetRequest::decode_json(obj);
+}
+
+void ceph::io_exerciser::json::OSDPoolGetRequest::decode_json(JSONObj* obj) {
+ JSONDecoder::decode_json("prefix", prefix, obj);
+ JSONDecoder::decode_json("pool", pool, obj);
+ JSONDecoder::decode_json("var", var, obj);
+ JSONDecoder::decode_json("format", format, obj);
+}
+
+void ceph::io_exerciser::json::OSDPoolGetRequest::dump() const
+{
+ formatter->open_object_section("OSDPoolGetRequest");
+ ::encode_json("prefix", prefix, formatter.get());
+ ::encode_json("pool", pool, formatter.get());
+ ::encode_json("var", var, formatter.get());
+ ::encode_json("format", format, formatter.get());
+ formatter->close_section();
+}
+
+ceph::io_exerciser::json::OSDPoolGetReply
+ ::OSDPoolGetReply(std::shared_ptr<ceph::Formatter> formatter) :
+ JSONStructure(formatter)
+{
+
+}
+
+void ceph::io_exerciser::json::OSDPoolGetReply::decode_json(JSONObj* obj) {
+ JSONDecoder::decode_json("erasure_code_profile", erasure_code_profile, obj);
+}
+
+void ceph::io_exerciser::json::OSDPoolGetReply::dump() const
+{
+ formatter->open_object_section("OSDPoolGetReply");
+ ::encode_json("erasure_code_profile", erasure_code_profile, formatter.get());
+ formatter->close_section();
+}
+
+ceph::io_exerciser::json::OSDECProfileGetRequest
+ ::OSDECProfileGetRequest(const std::string& profile_name,
+ std::shared_ptr<ceph::Formatter> formatter) :
+ JSONStructure(formatter),
+ name(profile_name)
+{
+
+}
+
+ceph::io_exerciser::json::OSDECProfileGetRequest
+ ::OSDECProfileGetRequest(std::shared_ptr<ceph::Formatter> formatter) :
+ JSONStructure(formatter)
+{
+
+}
+
+void ceph::io_exerciser::json::OSDECProfileGetRequest::decode_json(JSONObj* obj) {
+ JSONDecoder::decode_json("prefix", prefix, obj);
+ JSONDecoder::decode_json("name", name, obj);
+ JSONDecoder::decode_json("format", format, obj);
+}
+
+void ceph::io_exerciser::json::OSDECProfileGetRequest::dump() const
+{
+ formatter->open_object_section("OSDECProfileGetRequest");
+ ::encode_json("prefix", prefix, formatter.get());
+ ::encode_json("name", name, formatter.get());
+ ::encode_json("format", format, formatter.get());
+ formatter->close_section();
+}
+
+ceph::io_exerciser::json::OSDECProfileGetReply
+ ::OSDECProfileGetReply(std::shared_ptr<ceph::Formatter> formatter) :
+ JSONStructure(formatter)
+{
+
+}
+
+void ceph::io_exerciser::json::OSDECProfileGetReply::decode_json(JSONObj* obj) {
+ JSONDecoder::decode_json("crush-device-class", crush_device_class, obj);
+ JSONDecoder::decode_json("crush-failure-domain", crush_failure_domain, obj);
+ JSONDecoder::decode_json("crush-num-failure-domains",
+ crush_num_failure_domains,
+ 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("k", k, obj);
+ JSONDecoder::decode_json("m", m, obj);
+ JSONDecoder::decode_json("plugin", plugin, obj);
+ JSONDecoder::decode_json("technique", technique, obj);
+ JSONDecoder::decode_json("w", w, obj);
+}
+
+void ceph::io_exerciser::json::OSDECProfileGetReply::dump() const
+{
+ formatter->open_object_section("OSDECProfileGetReply");
+ ::encode_json("crush-device-class",
+ crush_device_class,
+ formatter.get());
+ ::encode_json("crush-failure-domain",
+ crush_failure_domain,
+ formatter.get());
+ ::encode_json("crush-num-failure-domains",
+ crush_num_failure_domains,
+ formatter.get());
+ ::encode_json("crush-osds-per-failure-domain",
+ crush_osds_per_failure_domain,
+ formatter.get());
+ ::encode_json("crush-root", crush_root, formatter.get());
+ ::encode_json("jerasure-per-chunk-alignment",
+ jerasure_per_chunk_alignment,
+ formatter.get());
+ ::encode_json("k", k, formatter.get());
+ ::encode_json("m", m, formatter.get());
+ ::encode_json("plugin", plugin, formatter.get());
+ ::encode_json("technique", technique, formatter.get());
+ ::encode_json("w", w, formatter.get());
+ formatter->close_section();
+}
+
+ceph::io_exerciser::json::OSDECProfileSetRequest
+ ::OSDECProfileSetRequest(const std::string& name,
+ const std::vector<std::string>& profile,
+ std::shared_ptr<ceph::Formatter> formatter) :
JSONStructure(formatter),
name(name),
profile(profile)
::OSDECPoolCreateRequest(std::shared_ptr<ceph::Formatter> formatter) :
JSONStructure(formatter)
{
-
}
void OSDECPoolCreateRequest::decode_json(JSONObj* obj)
OSDSetRequest::OSDSetRequest(std::shared_ptr<ceph::Formatter> formatter) :
JSONStructure(formatter)
{
-
}
void OSDSetRequest::decode_json(JSONObj* obj)
}
-void BalancerOffRequest::decode_json(JSONObj* obj)
+void ceph::io_exerciser::json::BalancerOffRequest::decode_json(JSONObj* obj)
{
JSONDecoder::decode_json("prefix", prefix, obj);
}
}
-void BalancerStatusRequest::decode_json(JSONObj* obj)
+void ceph::io_exerciser::json::BalancerStatusRequest::decode_json(JSONObj* obj)
{
JSONDecoder::decode_json("prefix", prefix, obj);
}
{
switch(injectOpType)
{
- case InjectOpType::Read:
+ case InjectOpType::ReadEIO:
+ [[ fallthrough ]];
+ case InjectOpType::ReadMissingShard:
prefix = "injectecreaderr";
break;
- case InjectOpType::Write:
+ case InjectOpType::WriteFailAndRollback:
+ [[ fallthrough ]];
+ case InjectOpType::WriteOSDAbort:
prefix = "injectecwriteerr";
break;
+ default:
+ ceph_abort_msg("Invalid OP type to inject");
}
}
{
switch(injectOpType)
{
- case InjectOpType::Read:
+ case InjectOpType::ReadEIO:
+ [[ fallthrough ]];
+ case InjectOpType::ReadMissingShard:
prefix = "injectecclearreaderr";
break;
- case InjectOpType::Write:
+ case InjectOpType::WriteFailAndRollback:
+ [[ fallthrough ]];
+ case InjectOpType::WriteOSDAbort:
prefix = "injectecclearwriteerr";
break;
+ default:
+ ceph_abort_msg("Invalid OP type to inject");
}
}
#include "include/types.h"
+#include "OpType.h"
+
/* Overview
*
* class JSONStructure
void dump() const;
};
+ class OSDPoolGetRequest : public JSONStructure
+ {
+ public:
+ OSDPoolGetRequest(const std::string& pool_name, std::shared_ptr<ceph::Formatter> formatter = std::make_shared<JSONFormatter>(false));
+ OSDPoolGetRequest(JSONObj* obj, std::shared_ptr<ceph::Formatter> formatter = std::make_shared<JSONFormatter>(false));
+
+ std::string prefix = "osd pool get";
+ std::string pool;
+ std::string var = "erasure_code_profile";
+ std::string format = "json";
+
+ void decode_json(JSONObj* obj) override;
+ void dump() const override;
+ };
+
+ class OSDPoolGetReply : public JSONStructure
+ {
+ public:
+ OSDPoolGetReply(std::shared_ptr<ceph::Formatter> formatter = std::make_shared<JSONFormatter>(false));
+
+ std::string erasure_code_profile;
+
+ void decode_json(JSONObj *obj);
+ void dump() const;
+ };
+
+ class OSDECProfileGetRequest : public JSONStructure
+ {
+ public:
+ OSDECProfileGetRequest(const std::string& profile_name, std::shared_ptr<ceph::Formatter> formatter = std::make_shared<JSONFormatter>(false));
+ OSDECProfileGetRequest(std::shared_ptr<ceph::Formatter> formatter = std::make_shared<JSONFormatter>(false));
+
+ std::string prefix = "osd pool get";
+ std::string name;
+ std::string format = "json";
+
+ void decode_json(JSONObj* obj) override;
+ void dump() const override;
+ };
+
+ class OSDECProfileGetReply : public JSONStructure
+ {
+ public:
+ OSDECProfileGetReply(std::shared_ptr<ceph::Formatter> formatter = std::make_shared<JSONFormatter>(false));
+
+ std::string crush_device_class;
+ std::string crush_failure_domain;
+ int crush_num_failure_domains;
+ int crush_osds_per_failure_domain;
+ std::string crush_root;
+ bool jerasure_per_chunk_alignment;
+ int k;
+ int m;
+ std::string plugin;
+ std::string technique;
+ std::string w;
+
+ void decode_json(JSONObj *obj);
+ void dump() const;
+ };
+
class OSDECProfileSetRequest : public JSONStructure
{
public:
OSDECProfileSetRequest(const std::string& name,
- std::vector<std::string> profile,
+ const std::vector<std::string>& profile,
std::shared_ptr<Formatter> formatter
= std::make_shared<JSONFormatter>(false));
OSDECProfileSetRequest(std::shared_ptr<Formatter> formatter
void dump() const override;
};
- enum class InjectOpType
- {
- Read,
- Write
- };
-
class InjectECErrorRequest : public JSONStructure
{
public:
ClearReadErrorInject, // Op to tell OSD to clear read error injects
ClearWriteErrorInject // Op to tell OSD to clear write error injects
};
+
+ enum class InjectOpType {
+ None,
+ ReadEIO,
+ ReadMissingShard,
+ WriteFailAndRollback,
+ WriteOSDAbort
+ };
}
}
auto formatter = std::make_shared<JSONFormatter>(false);
int osd = -1;
+ std::vector<int> shard_order;
ceph::io_exerciser::json::OSDMapRequest osdMapRequest(pool,
get_oid(),
reply.decode_json(&p);
osd = reply.acting_primary;
+ shard_order = reply.acting;
+
+ InjectOpType injectOpType;
switch(op.getOpType())
{
{
InjectReadErrorOp& errorOp = static_cast<InjectReadErrorOp&>(op);
- ceph::io_exerciser::json::InjectECErrorRequest injectErrorRequest(json::InjectOpType::Read,
+ if (errorOp.type == 0)
+ {
+ injectOpType = InjectOpType::ReadEIO;
+ }
+ else if (errorOp.type == 1)
+ {
+ injectOpType = InjectOpType::ReadMissingShard;
+ }
+ else
+ {
+ ceph_abort_msg("Unsupported inject type");
+ }
+
+ ceph::io_exerciser::json::InjectECErrorRequest injectErrorRequest(injectOpType,
pool,
oid,
errorOp.shard,
{
InjectWriteErrorOp& errorOp = static_cast<InjectWriteErrorOp&>(op);
- ceph::io_exerciser::json::InjectECErrorRequest injectErrorRequest(json::InjectOpType::Write,
+ if (errorOp.type == 0)
+ {
+ injectOpType = InjectOpType::WriteFailAndRollback;
+ }
+ else if (errorOp.type == 3)
+ {
+ injectOpType = InjectOpType::WriteOSDAbort;
+
+ // This inject is sent directly to the shard we want to inject the error on
+ osd = shard_order[errorOp.shard];
+ }
+ else
+ {
+ ceph_abort("Unsupported inject type");
+ }
+
+ ceph::io_exerciser::json::InjectECErrorRequest injectErrorRequest(injectOpType,
pool,
oid,
errorOp.shard,
{
ClearReadErrorInjectOp& errorOp = static_cast<ClearReadErrorInjectOp&>(op);
- ceph::io_exerciser::json::InjectECClearErrorRequest clearErrorInject(json::InjectOpType::Read,
+ if (errorOp.type == 0)
+ {
+ injectOpType = InjectOpType::ReadEIO;
+ }
+ else if (errorOp.type == 1)
+ {
+ injectOpType = InjectOpType::ReadMissingShard;
+ }
+ else
+ {
+ ceph_abort("Unsupported inject type");
+ }
+
+ ceph::io_exerciser::json::InjectECClearErrorRequest clearErrorInject(injectOpType,
pool,
oid,
errorOp.shard,
{
ClearReadErrorInjectOp& errorOp = static_cast<ClearReadErrorInjectOp&>(op);
- ceph::io_exerciser::json::InjectECClearErrorRequest clearErrorInject(json::InjectOpType::Write,
+ if (errorOp.type == 0)
+ {
+ injectOpType = InjectOpType::WriteFailAndRollback;
+ }
+ else if (errorOp.type == 3)
+ {
+ injectOpType = InjectOpType::WriteOSDAbort;
+ }
+ else
+ {
+ ceph_abort("Unsupported inject type");
+ }
+
+ ceph::io_exerciser::json::InjectECClearErrorRequest clearErrorInject(injectOpType,
pool,
oid,
errorOp.shard,
#include "common/io_exerciser/RadosIo.h"
#include "common/io_exerciser/IoOp.h"
#include "common/io_exerciser/IoSequence.h"
+#include "common/io_exerciser/EcIoSequence.h"
#include "common/io_exerciser/JsonStructures.h"
#include "json_spirit/json_spirit.h"
"number of threads of I/O per object (default 1)")
("parallel,p", po::value<int>()->default_value(1),
"number of objects to exercise in parallel")
+ ("testrecovery",
+ "Inject errors during sequences to test recovery processes of OSDs")
("interactive",
"interactive mode, execute IO commands from stdin")
("allow_pool_autoscaling",
if (force_value.has_value())
{
return *force_value;
- } else {
+ }
+ else
+ {
return std::make_pair(ceph::io_exerciser::Sequence::SEQUENCE_BEGIN,
ceph::io_exerciser::Sequence::SEQUENCE_END);
}
{
std::pair<int,int> value;
if (!skm.isForced() && force_value.has_value()) {
+ int rc;
+ bufferlist inbl, outbl;
+ auto formatter = std::make_shared<JSONFormatter>(false);
+
+ ceph::io_exerciser::json::OSDPoolGetRequest osdPoolGetRequest(*force_value, formatter);
+ rc = rados.mon_command(osdPoolGetRequest.encode_json(), inbl, &outbl, nullptr);
+ ceph_assert(rc == 0);
+
+ JSONParser p;
+ bool success = p.parse(outbl.c_str(), outbl.length());
+ ceph_assert(success);
+
+ ceph::io_exerciser::json::OSDPoolGetReply osdPoolGetReply(formatter);
+ osdPoolGetReply.decode_json(&p);
+
+ ceph::io_exerciser::json::OSDECProfileGetRequest osdECProfileGetRequest(osdPoolGetReply.erasure_code_profile, formatter);
+ rc = rados.mon_command(osdECProfileGetRequest.encode_json(), inbl, &outbl, nullptr);
+ ceph_assert(rc == 0);
+
+ success = p.parse(outbl.c_str(), outbl.length());
+ ceph_assert(success);
+
+ ceph::io_exerciser::json::OSDECProfileGetReply reply(formatter);
+ reply.decode_json(&p);
+ k = reply.k;
+ m = reply.m;
return *force_value;
} else {
value = skm.choose();
}
- int k = value.first;
- int m = value.second;
+ k = value.first;
+ m = value.second;
const std::string plugin = std::string(spl.choose());
const uint64_t chunk_size = scs.choose();
ceph::condition_variable& cond,
bool dryrun,
bool verbose,
- std::optional<int> seqseed) :
- rng(rng), verbose(verbose), seqseed(seqseed)
+ std::optional<int> seqseed,
+ bool testrecovery) :
+ rng(rng), verbose(verbose), seqseed(seqseed), testrecovery(testrecovery)
{
if (dryrun) {
- verbose = true;
exerciser_model = std::make_unique<ceph::io_exerciser::ObjectModel>(oid,
sbs.choose(),
rng());
} else {
const std::string pool = spo.choose();
+ poolK = spo.getChosenK();
+ poolM = spo.getChosenM();
+
int threads = snt.choose();
bufferlist inbl, outbl;
obj_size_range = sos.choose();
seq_range = ssr.choose();
curseq = seq_range.first;
- seq = ceph::io_exerciser::IoSequence::generate_sequence(curseq,
- obj_size_range,
- seqseed.value_or(rng()));
+
+ if (testrecovery)
+ {
+ seq = ceph::io_exerciser::EcIoSequence::generate_sequence(curseq,
+ obj_size_range,
+ poolK,
+ poolM,
+ seqseed.value_or(rng()));
+ }
+ else
+ {
+ seq = ceph::io_exerciser::IoSequence::generate_sequence(curseq,
+ obj_size_range,
+ seqseed.value_or(rng()));
+ }
+
op = seq->next();
done = false;
dout(0) << "== " << exerciser_model->get_oid() << " "
<< curseq << " "
- << seq->get_name()
+ << seq->get_name_with_seqseed()
<< " ==" <<dendl;
}
exerciser_model->applyIoOp(*op);
if (op->getOpType() == ceph::io_exerciser::OpType::Done)
{
- ++curseq;
- if (curseq == seq_range.second)
+ curseq = seq->getNextSupportedSequenceId();
+ if (curseq >= seq_range.second)
{
done = true;
dout(0) << exerciser_model->get_oid()
}
else
{
- seq = ceph::io_exerciser::IoSequence::generate_sequence(curseq,
- obj_size_range,
- seqseed.value_or(rng()));
+ if (testrecovery)
+ {
+ seq = ceph::io_exerciser::EcIoSequence::generate_sequence(curseq,
+ obj_size_range,
+ poolK, poolM,
+ seqseed.value_or(rng()));
+ }
+ else
+ {
+ seq = ceph::io_exerciser::IoSequence::generate_sequence(curseq,
+ obj_size_range,
+ seqseed.value_or(rng()));
+ }
+
dout(0) << "== " << exerciser_model->get_oid() << " "
- << curseq << " " << seq->get_name()
+ << curseq << " " << seq->get_name_with_seqseed()
<< " ==" <<dendl;
op = seq->next();
}
num_objects = vm["parallel"].as<int>();
object_name = vm["object"].as<std::string>();
interactive = vm.contains("interactive");
+ testrecovery = vm.contains("testrecovery");
allow_pool_autoscaling = vm.contains("allow_pool_autoscaling");
allow_pool_balancer = vm.contains("allow_pool_balancer");
}
}
-void ceph::io_sequence::tester::TestRunner::list_sequence()
+void ceph::io_sequence::tester::TestRunner::list_sequence(bool testrecovery)
{
// List seqeunces
std::pair<int,int> obj_size_range = sos.choose();
- for (ceph::io_exerciser::Sequence s
- = ceph::io_exerciser::Sequence::SEQUENCE_BEGIN;
- s < ceph::io_exerciser::Sequence::SEQUENCE_END; ++s)
+ ceph::io_exerciser::Sequence s = ceph::io_exerciser::Sequence::SEQUENCE_BEGIN;
+ std::unique_ptr<ceph::io_exerciser::IoSequence> seq;
+ if (testrecovery)
{
- std::unique_ptr<ceph::io_exerciser::IoSequence> seq =
- ceph::io_exerciser::IoSequence::generate_sequence(s,
- obj_size_range,
- seqseed.value_or(rng()));
- dout(0) << s << " " << seq->get_name() << dendl;
+ seq = ceph::io_exerciser::EcIoSequence::generate_sequence(s, obj_size_range,
+ spo.getChosenK(),
+ spo.getChosenM(),
+ seqseed.value_or(rng()));
}
+ else
+ {
+ seq = ceph::io_exerciser::IoSequence::generate_sequence(s, obj_size_range,
+ seqseed.value_or(rng()));
+ }
+
+ do
+ {
+ dout(0) << s << " " << seq->get_name_with_seqseed() << dendl;
+ s = seq->getNextSupportedSequenceId();
+ } while (s != ceph::io_exerciser::Sequence::SEQUENCE_END);
}
void ceph::io_sequence::tester::TestRunner::clear_tokens()
}
else if (show_sequence)
{
- list_sequence();
+ list_sequence(testrecovery);
return true;
}
else if (interactive)
sbs, spo, sos, snt, ssr,
rng, lock, cond,
dryrun, verbose,
- seqseed
+ seqseed, testrecovery
)
);
}
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; }
private:
void create_pool(librados::Rados& rados,
bool allow_pool_balancer;
bool allow_pool_deep_scrubbing;
bool allow_pool_scrubbing;
-
+ int k;
+ int m;
+
SelectErasureKM skm;
SelectErasurePlugin spl;
SelectErasureChunkSize scs;
ceph::condition_variable& cond,
bool dryrun,
bool verbose,
- std::optional<int> seqseed);
+ std::optional<int> seqseed,
+ bool testRecovery);
int get_num_io();
bool readyForIo();
ceph::util::random_number_generator<int>& rng;
bool verbose;
std::optional<int> seqseed;
+ int poolK;
+ int poolM;
+ bool testrecovery;
};
class TestRunner
std::optional<int> seqseed;
bool interactive;
+ bool testrecovery;
+
bool allow_pool_autoscaling;
bool allow_pool_balancer;
bool allow_pool_deep_scrubbing;
bool run_interactive_test();
void help();
- void list_sequence();
+ void list_sequence(bool testrecovery);
};
}
}