}
if (!invalid_block_offsets.empty()) {
- dout(0) << "Miscompare for read of " << m_model.get_oid() <<
+ dout(0) << "Miscompare for read of " << m_model.get_primary_oid() <<
" offset=" << offset << " length=" << length << dendl;
printDebugInformationForOffsets(offset, invalid_block_offsets, bufferlist);
}
using CreateOp = ceph::io_exerciser::CreateOp;
using RemoveOp = ceph::io_exerciser::RemoveOp;
using ConsistencyOp = ceph::io_exerciser::ConsistencyOp;
+using SwapOp = ceph::io_exerciser::SwapOp;
+using CopyOp = ceph::io_exerciser::CopyOp;
using SingleReadOp = ceph::io_exerciser::SingleReadOp;
using DoubleReadOp = ceph::io_exerciser::DoubleReadOp;
using TripleReadOp = ceph::io_exerciser::TripleReadOp;
std::string RemoveOp::to_string(uint64_t block_size) const { return "Remove"; }
+SwapOp::SwapOp() : TestOp<OpType::Swap>() {}
+
+std::unique_ptr<SwapOp> SwapOp::generate() {
+ return std::make_unique<SwapOp>();
+}
+
+std::string SwapOp::to_string(uint64_t block_size) const { return "Swap"; }
+
+CopyOp::CopyOp() : TestOp<OpType::Copy>() {}
+
+std::unique_ptr<CopyOp> CopyOp::generate() {
+ return std::make_unique<CopyOp>();
+}
+
+std::string CopyOp::to_string(uint64_t block_size) const { return "Copy"; }
+
template <OpType opType, int numIOs>
ceph::io_exerciser::ReadWriteOp<opType, numIOs>::ReadWriteOp(
std::array<uint64_t, numIOs>&& offset,
std::string to_string(uint64_t block_size) const override;
};
+class SwapOp : public TestOp<OpType::Swap> {
+ public:
+ SwapOp();
+ static std::unique_ptr<SwapOp> generate();
+ std::string to_string(uint64_t block_size) const override;
+};
+
+class CopyOp : public TestOp<OpType::Copy> {
+ public:
+ CopyOp();
+ static std::unique_ptr<CopyOp> generate();
+ std::string to_string(uint64_t block_size) const override;
+};
+
template <OpType opType, int numIOs>
class ReadWriteOp : public TestOp<opType> {
public:
}
};
} // namespace io_exerciser
-} // namespace ceph
\ No newline at end of file
+} // namespace ceph
case Sequence::SEQUENCE_SEQ14:
os << "SEQUENCE_SEQ14";
break;
+ case Sequence::SEQUENCE_SEQ15:
+ os << "SEQUENCE_SEQ15";
+ break;
case Sequence::SEQUENCE_END:
os << "SEQUENCE_END";
break;
return std::make_unique<Seq13>(obj_size_range, seed, check_consistency);
case Sequence::SEQUENCE_SEQ14:
return std::make_unique<Seq14>(obj_size_range, seed, check_consistency);
+ case Sequence::SEQUENCE_SEQ15:
+ return std::make_unique<Seq15>(obj_size_range, seed, check_consistency);
default:
break;
}
consistency_in_progress(false),
consistency_request_sent(false),
check_consistency(check_consistency),
+ swap(false),
obj_size(min_obj_size),
step(-1),
seed(seed) {
if (done) {
return DoneOp::generate();
}
+ if (swap) {
+ swap = false;
+ return SwapOp::generate();
+ }
if (create) {
create = false;
barrier = true;
}
return r;
}
+
+ceph::io_exerciser::Seq15::Seq15(std::pair<int, int> obj_size_range, int seed, bool check_consistency)
+ : IoSequence(obj_size_range, seed, check_consistency), offset(0) {
+ select_random_object_size();
+ if (obj_size < 4) {
+ obj_size = 4;
+ }
+ primary_size = obj_size;
+ secondary_size = primary_size - 3;
+ }
+
+Sequence ceph::io_exerciser::Seq15::get_id() const {
+ return Sequence::SEQUENCE_SEQ15;
+}
+
+std::string ceph::io_exerciser::Seq15::get_name() const {
+ return "Different permutations of writes to objects of different sizes, then copy, resize and read";
+}
+
+std::unique_ptr<ceph::io_exerciser::IoOp> ceph::io_exerciser::Seq15::_next() {
+ std::unique_ptr<IoOp> r = BarrierOp::generate();
+
+ using Stage = ceph::io_exerciser::Seq15::Stage;
+ auto next_stage = [this]() {
+ stage = static_cast<Stage>(static_cast<int>(stage)+1);
+ };
+ switch (stage) {
+ case Stage::WRITE_PRIMARY:
+ // Seq0 with writes instead of reads
+ length = 1 + rng(obj_size - 1);
+ if (offset >= obj_size) {
+ offset = 0;
+ next_stage();
+ break;
+ }
+ if (offset + length > obj_size) {
+ r = SingleWriteOp::generate(offset, obj_size - offset);
+ } else {
+ r = SingleWriteOp::generate(offset, length);
+ }
+ offset += length;
+ break;
+ case Stage::CREATE_SECONDARY:
+ obj_size = secondary_size;
+ create = true;
+ r = SwapOp::generate();
+ next_stage();
+ break;
+ case Stage::WRITE_SECONDARY:
+ // Seq9
+ if (!doneread) {
+ if (!donebarrier) {
+ donebarrier = true;
+ r = BarrierOp::generate();
+ break;
+ }
+ doneread = true;
+ barrier = true;
+ r = SingleReadOp::generate(0, obj_size);
+ break;
+ }
+ length++;
+ if (length > obj_size - offset) {
+ length = 1;
+ offset++;
+ if (offset >= obj_size) {
+ offset = 0;
+ next_stage();
+ break;
+ }
+ }
+ doneread = false;
+ donebarrier = false;
+ r = SingleWriteOp::generate(offset, length);
+ break;
+ case Stage::COPY_FROM_SECONDARY:
+ r = CopyOp::generate();
+ next_stage();
+ break;
+ case Stage::READ_SECONDARY:
+ r = SingleReadOp::generate(0, obj_size);
+ next_stage();
+ break;
+ case Stage::SWAP_TO_PRIMARY:
+ r = SwapOp::generate();
+ next_stage();
+ break;
+ case Stage::TRUNCATE_PRIMARY:
+ obj_size = obj_size + 2;
+ r = TruncateOp::generate(obj_size);
+ next_stage();
+ break;
+ case Stage::READ_PRIMARY:
+ r = SingleReadOp::generate(0, obj_size);
+ next_stage();
+ break;
+ case Stage::DONE:
+ [[fallthrough]];
+ default:
+ done = true;
+ break;
+ }
+
+ return r;
+}
SEQUENCE_SEQ12,
SEQUENCE_SEQ13,
SEQUENCE_SEQ14,
+ SEQUENCE_SEQ15,
SEQUENCE_END,
SEQUENCE_BEGIN = SEQUENCE_SEQ0
bool consistency_in_progress;
bool consistency_request_sent;
bool check_consistency;
+ bool swap;
uint64_t obj_size;
int step;
int seed;
std::string get_name() const override;
std::unique_ptr<IoOp> _next() override;
};
+
+class Seq15 : public IoSequence {
+ private:
+ uint64_t offset;
+ uint64_t length;
+ uint64_t primary_size;
+ uint64_t secondary_size;
+ bool doneread = true;
+ bool donebarrier = false;
+ enum class Stage {
+ WRITE_PRIMARY,
+ CREATE_SECONDARY,
+ WRITE_SECONDARY,
+ COPY_FROM_SECONDARY,
+ READ_SECONDARY,
+ SWAP_TO_PRIMARY,
+ TRUNCATE_PRIMARY,
+ READ_PRIMARY,
+ DONE
+ };
+ Stage stage;
+
+ public:
+ Seq15(std::pair<int, int> obj_size_range, int seed, bool check_consistency);
+
+ Sequence get_id() const override;
+ std::string get_name() const override;
+ std::unique_ptr<IoOp> _next() override;
+};
} // namespace io_exerciser
} // namespace ceph
using Model = ceph::io_exerciser::Model;
-Model::Model(const std::string& oid, uint64_t block_size)
- : num_io(0), oid(oid), block_size(block_size) {}
+Model::Model(const std::string& primary_oid, const std::string& secondary_oid, uint64_t block_size)
+ : num_io(0), primary_oid(primary_oid), secondary_oid(secondary_oid), block_size(block_size) {}
-const uint64_t Model::get_block_size() const { return block_size; }
+const std::string Model::get_primary_oid() const { return primary_oid; }
+
+const std::string Model::get_secondary_oid() const { return secondary_oid; }
+
+void Model::set_primary_oid(const std::string& new_oid) {
+ primary_oid = new_oid;
+}
-const std::string Model::get_oid() const { return oid; }
+void Model::set_secondary_oid(const std::string& new_oid) {
+ secondary_oid = new_oid;
+}
+
+void Model::swap_primary_secondary_oid() {
+ std::string old_primary;
+ old_primary = Model::get_primary_oid();
+ Model::set_primary_oid(Model::get_secondary_oid());
+ Model::set_secondary_oid(old_primary);
+}
+
+const uint64_t Model::get_block_size() const { return block_size; }
int Model::get_num_io() const { return num_io; }
\ No newline at end of file
class Model {
protected:
int num_io{0};
- std::string oid;
+ std::string primary_oid;
+ std::string secondary_oid;
uint64_t block_size;
+ void set_primary_oid(const std::string& new_oid);
+ void set_secondary_oid(const std::string& new_oid);
+
public:
- Model(const std::string& oid, uint64_t block_size);
+ Model(const std::string& primary_oid, const std::string& secondary_oid, uint64_t block_size);
virtual ~Model() = default;
virtual bool readyForIoOp(IoOp& op) = 0;
virtual void applyIoOp(IoOp& op) = 0;
- const std::string get_oid() const;
+ const std::string get_primary_oid() const;
+ const std::string get_secondary_oid() const;
+ void swap_primary_secondary_oid();
const uint64_t get_block_size() const;
int get_num_io() const;
};
using ObjectModel = ceph::io_exerciser::ObjectModel;
-ObjectModel::ObjectModel(const std::string& oid, uint64_t block_size, int seed)
- : Model(oid, block_size), created(false) {
+ObjectModel::ObjectModel(const std::string& primary_oid, const std::string& secondary_oid, uint64_t block_size, int seed)
+ : Model(primary_oid, secondary_oid, block_size), primary_created(false), secondary_created(false) {
rng.seed(seed);
}
int ObjectModel::get_seed(uint64_t offset) const {
- ceph_assert(offset < contents.size());
- return contents[offset];
+ ceph_assert(offset < primary_contents.size());
+ return primary_contents[offset];
}
std::vector<int> ObjectModel::get_seed_offsets(int seed) const {
std::vector<int> offsets;
- for (size_t i = 0; i < contents.size(); i++) {
- if (contents[i] == seed) {
+ for (size_t i = 0; i < primary_contents.size(); i++) {
+ if (primary_contents[i] == seed) {
offsets.push_back(i);
}
}
}
std::string ObjectModel::to_string(int mask) const {
- if (!created) {
+ if (!primary_created) {
return "Object does not exist";
}
std::string result = "{";
- for (uint64_t i = 0; i < contents.size(); i++) {
+ for (uint64_t i = 0; i < primary_contents.size(); i++) {
if (i != 0) {
result += ",";
}
- result += std::to_string(contents[i] & mask);
+ result += std::to_string(primary_contents[i] & mask);
}
result += "}";
return result;
};
auto verify_and_record_read_op =
- [&contents = contents, &created = created, &num_io = num_io,
+ [&primary_contents = primary_contents,
+ &primary_created = primary_created,
+ &num_io = num_io,
&reads = reads,
&writes = writes]<OpType opType, int N>(ReadWriteOp<opType, N>& readOp) {
- ceph_assert(created);
+ ceph_assert(primary_created);
for (int i = 0; i < N; i++) {
- ceph_assert(readOp.offset[i] + readOp.length[i] <= contents.size());
+ ceph_assert(readOp.offset[i] + readOp.length[i] <= primary_contents.size());
// Not allowed: read overlapping with parallel write
ceph_assert(!writes.intersects(readOp.offset[i], readOp.length[i]));
reads.union_insert(readOp.offset[i], readOp.length[i]);
};
auto verify_write_and_record_and_generate_seed =
- [&generate_random, &contents = contents, &created = created,
- &num_io = num_io, &reads = reads,
+ [&generate_random, &primary_contents = primary_contents,
+ &primary_created = primary_created,
+ &num_io = num_io,
+ &reads = reads,
&writes = writes]<OpType opType, int N>(ReadWriteOp<opType, N> writeOp) {
- ceph_assert(created);
+ ceph_assert(primary_created);
for (int i = 0; i < N; i++) {
// Not allowed: write overlapping with parallel read or write
ceph_assert(!reads.intersects(writeOp.offset[i], writeOp.length[i]));
ceph_assert(!writes.intersects(writeOp.offset[i], writeOp.length[i]));
writes.union_insert(writeOp.offset[i], writeOp.length[i]);
- if (writeOp.offset[i] + writeOp.length[i] > contents.size()) {
- contents.resize(writeOp.offset[i] + writeOp.length[i]);
+ if (writeOp.offset[i] + writeOp.length[i] > primary_contents.size()) {
+ primary_contents.resize(writeOp.offset[i] + writeOp.length[i]);
}
std::generate(std::execution::seq,
- std::next(contents.begin(), writeOp.offset[i]),
- std::next(contents.begin(),
+ std::next(primary_contents.begin(), writeOp.offset[i]),
+ std::next(primary_contents.begin(),
writeOp.offset[i] + writeOp.length[i]),
generate_random);
}
};
auto verify_failed_write_and_record =
- [&contents = contents, &created = created, &num_io = num_io,
+ [&primary_contents = primary_contents,
+ &primary_created = primary_created,
+ &num_io = num_io,
&reads = reads,
&writes = writes]<OpType opType, int N>(ReadWriteOp<opType, N> writeOp) {
// Ensure write should still be valid, even though we are expecting OSD
// failure
- ceph_assert(created);
+ ceph_assert(primary_created);
for (int i = 0; i < N; i++) {
// Not allowed: write overlapping with parallel read or write
ceph_assert(!reads.intersects(writeOp.offset[i], writeOp.length[i]));
ceph_assert(!writes.intersects(writeOp.offset[i], writeOp.length[i]));
writes.union_insert(writeOp.offset[i], writeOp.length[i]);
- ceph_assert(writeOp.offset[i] + writeOp.length[i] <= contents.size());
+ ceph_assert(writeOp.offset[i] + writeOp.length[i] <= primary_contents.size());
}
num_io++;
};
writes.clear();
break;
+ case OpType::Swap: {
+ bool temp = primary_created;
+ primary_created = secondary_created;
+ secondary_created = temp;
+ primary_contents.swap(secondary_contents);
+ reads.clear();
+ writes.clear();
+ } break;
+
+ case OpType::Copy:
+ ceph_assert(primary_created && secondary_created);
+ ceph_assert(reads.empty());
+ ceph_assert(writes.empty());
+ // The target object may be larger than the source - however, it will be replaced by a new object rather than overwriting
+ // and padding the old object. Therefore, the target object should now be the same size as the source object.
+ secondary_contents.resize(primary_contents.size());
+ std::copy(primary_contents.begin(), primary_contents.end(), secondary_contents.begin());
+ break;
+
case OpType::Create:
- ceph_assert(!created);
+ ceph_assert(!primary_created);
ceph_assert(reads.empty());
ceph_assert(writes.empty());
- created = true;
- contents.resize(static_cast<CreateOp&>(op).size);
- std::generate(std::execution::seq, contents.begin(), contents.end(),
+ primary_created = true;
+ primary_contents.resize(static_cast<CreateOp&>(op).size);
+ std::generate(std::execution::seq, primary_contents.begin(), primary_contents.end(),
generate_random);
break;
- case OpType::Truncate:
- ceph_assert(created);
+ case OpType::Truncate: {
+ ceph_assert(primary_created);
ceph_assert(reads.empty());
ceph_assert(writes.empty());
- contents.resize(static_cast<TruncateOp&>(op).size);
- break;
+ auto new_size = static_cast<TruncateOp&>(op).size;
+ auto old_size = primary_contents.size();
+ bool expand = new_size > old_size;
+ primary_contents.resize(new_size);
+ // Yes, truncate CAN be used to make an object bigger!
+ if (expand) {
+ std::generate(std::execution::seq, primary_contents.begin() + new_size, primary_contents.end(),
+ generate_random);
+ }
+ } break;
case OpType::Remove:
- ceph_assert(created);
+ ceph_assert(primary_created);
ceph_assert(reads.empty());
ceph_assert(writes.empty());
- created = false;
- contents.resize(0);
+ primary_created = false;
+ primary_contents.resize(0);
break;
-
+
case OpType::Read: {
SingleReadOp& readOp = static_cast<SingleReadOp&>(op);
verify_and_record_read_op(readOp);
} break;
case OpType::Write: {
- ceph_assert(created);
+ ceph_assert(primary_created);
SingleWriteOp& writeOp = static_cast<SingleWriteOp&>(op);
verify_write_and_record_and_generate_seed(writeOp);
} break;
case OpType::Write2: {
+ ceph_assert(primary_created);
DoubleWriteOp& writeOp = static_cast<DoubleWriteOp&>(op);
verify_write_and_record_and_generate_seed(writeOp);
} break;
case OpType::Write3: {
+ ceph_assert(primary_created);
TripleWriteOp& writeOp = static_cast<TripleWriteOp&>(op);
verify_write_and_record_and_generate_seed(writeOp);
} break;
case OpType::Append: {
- ceph_assert(created);
+ ceph_assert(primary_created);
SingleAppendOp& appendOp = static_cast<SingleAppendOp&>(op);
- appendOp.offset[0] = contents.size();
+ appendOp.offset[0] = primary_contents.size();
verify_write_and_record_and_generate_seed(appendOp);
} break;
case OpType::FailedWrite: {
- ceph_assert(created);
+ ceph_assert(primary_created);
SingleWriteOp& writeOp = static_cast<SingleWriteOp&>(op);
verify_failed_write_and_record(writeOp);
} break;
case OpType::FailedWrite2: {
+ ceph_assert(primary_created);
DoubleWriteOp& writeOp = static_cast<DoubleWriteOp&>(op);
verify_failed_write_and_record(writeOp);
-
} break;
case OpType::FailedWrite3: {
+ ceph_assert(primary_created);
TripleWriteOp& writeOp = static_cast<TripleWriteOp&>(op);
verify_failed_write_and_record(writeOp);
} break;
void ObjectModel::encode(ceph::buffer::list& bl) const {
ENCODE_START(1, 1, bl);
- encode(created, bl);
- if (created) {
- encode(contents, bl);
+ encode(primary_created, bl);
+ if (primary_created) {
+ encode(primary_contents, bl);
}
ENCODE_FINISH(bl);
}
void ObjectModel::decode(ceph::buffer::list::const_iterator& bl) {
DECODE_START(1, bl);
DECODE_OLDEST(1);
- decode(created, bl);
- if (created) {
- decode(contents, bl);
- } else {
- contents.resize(0);
+ decode(primary_created, bl);
+ if (primary_created) {
+ decode(primary_contents, bl);
}
DECODE_FINISH(bl);
}
class ObjectModel : public Model {
private:
- bool created;
- std::vector<int> contents;
+ bool primary_created;
+ bool secondary_created;
+ std::vector<int> primary_contents;
+ std::vector<int> secondary_contents;
ceph::util::random_number_generator<int> rng =
ceph::util::random_number_generator<int>();
interval_set<uint64_t> writes;
public:
- ObjectModel(const std::string& oid, uint64_t block_size, int seed);
+ ObjectModel(const std::string& primary_oid, const std::string& secondary_oid, uint64_t block_size, int seed);
int get_seed(uint64_t offset) const;
std::vector<int> get_seed_offsets(int seed) const;
Create, // Create object and pattern with data
Remove, // Remove object
Consistency, // Check consistency of an object
+ Swap, // Swap primary and secondary object
Read, // Read
Read2, // Two reads in a single op
Read3, // Three reads in a single op
FailedWrite, // A write which should fail
FailedWrite2, // Two writes in one op which should fail
FailedWrite3, // Three writes in one op which should fail
+ Copy, // Copy from primary to secondary object
InjectReadError, // Op to tell OSD to inject read errors
InjectWriteError, // Op to tell OSD to inject write errors
ClearReadErrorInject, // Op to tell OSD to clear read error injects
return fmt::format_to(ctx.out(), "Remove");
case ceph::io_exerciser::OpType::Consistency:
return fmt::format_to(ctx.out(), "Consistency");
+ case ceph::io_exerciser::OpType::Swap:
+ return fmt::format_to(ctx.out(), "Swap");
+ case ceph::io_exerciser::OpType::Copy:
+ return fmt::format_to(ctx.out(), "Copy");
case ceph::io_exerciser::OpType::Read:
return fmt::format_to(ctx.out(), "Read");
case ceph::io_exerciser::OpType::Read2:
} // namespace
RadosIo::RadosIo(librados::Rados& rados, boost::asio::io_context& asio,
- const std::string& pool, const std::string& oid,
+ const std::string& pool, const std::string& primary_oid, const std::string& secondary_oid,
const std::optional<std::vector<int>>& cached_shard_order,
uint64_t block_size, int seed, int threads, ceph::mutex& lock,
ceph::condition_variable& cond, bool is_replicated_pool,
bool ec_optimizations)
- : Model(oid, block_size),
+ : Model(primary_oid, secondary_oid, block_size),
rados(rados),
asio(asio),
- om(std::make_unique<ObjectModel>(oid, block_size, seed)),
+ om(std::make_unique<ObjectModel>(primary_oid, secondary_oid, block_size, seed)),
db(data_generation::DataGenerator::create_generator(
data_generation::GenerationType::HeaderedSeededRandom, *om)),
pool(pool),
wait_for_io(0);
break;
+ case OpType::Swap:
+ swap_primary_secondary_oid();
+ break;
+
case OpType::Create: {
start_io();
uint64_t opSize = static_cast<CreateOp&>(op).size;
ceph_assert(ec == boost::system::errc::success);
finish_io();
};
- librados::async_operate(asio.get_executor(), io, oid,
+ librados::async_operate(asio.get_executor(), io, primary_oid,
std::move(wop), 0, nullptr, create_cb);
break;
}
ceph_assert(ec == boost::system::errc::success);
finish_io();
};
- librados::async_operate(asio.get_executor(), io, oid, std::move(wop), 0,
+ librados::async_operate(asio.get_executor(), io, primary_oid, std::move(wop), 0,
nullptr, truncate_cb);
break;
}
ceph_assert(ec == boost::system::errc::success);
finish_io();
};
- librados::async_operate(asio.get_executor(), io, oid,
+ librados::async_operate(asio.get_executor(), io, primary_oid,
std::move(wop), 0, nullptr, remove_cb);
break;
}
start_io();
ceph_assert(cc);
bool is_consistent =
- cc->single_read_and_check_consistency(oid, block_size, 0, 0);
+ cc->single_read_and_check_consistency(primary_oid, block_size, 0, 0);
if (!is_consistent) {
std::stringstream strstream;
cc->print_results(strstream);
}
ceph_assert(is_consistent);
finish_io();
+ }
+
+ case OpType::Copy: {
+ start_io();
+ auto op_info = std::make_shared<AsyncOpInfo<0>>();
+ librados::ObjectWriteOperation wop;
+ wop.copy_from(primary_oid.c_str(), io, io.get_last_version(), 0);
+ auto write_cb = [this](boost::system::error_code ec, version_t ver) {
+ ceph_assert(ec == boost::system::errc::success);
+ finish_io();
+ };
+ librados::async_operate(asio.get_executor(), io, secondary_oid,
+ std::move(wop), 0, nullptr, write_cb);
break;
}
}
finish_io();
};
- librados::async_operate(asio.get_executor(), io, oid,
+ librados::async_operate(asio.get_executor(), io, primary_oid,
std::move(rop), 0, nullptr, read_cb);
num_io++;
};
ceph_assert(ec == boost::system::errc::success);
finish_io();
};
- librados::async_operate(asio.get_executor(), io, oid,
+ librados::async_operate(asio.get_executor(), io, primary_oid,
std::move(wop), 0, nullptr, write_cb);
num_io++;
};
ceph_assert(ec != boost::system::errc::success);
finish_io();
};
- librados::async_operate(asio.get_executor(), io, oid,
+ librados::async_operate(asio.get_executor(), io, primary_oid,
std::move(wop), 0, nullptr, write_cb);
num_io++;
};
int osd = -1;
std::vector<int> shard_order;
- ceph::messaging::osd::OSDMapRequest osdMapRequest{pool, get_oid(), ""};
+ ceph::messaging::osd::OSDMapRequest osdMapRequest{pool, get_primary_oid(), ""};
int rc = send_mon_command(osdMapRequest, rados, "OSDMapRequest", osdmap_inbl,
&osdmap_outbl, formatter.get());
ceph_assert(rc == 0);
if (errorOp.type == 0) {
ceph::messaging::osd::InjectECErrorRequest<InjectOpType::ReadEIO>
- injectErrorRequest{pool, oid, errorOp.shard,
+ injectErrorRequest{pool, primary_oid, errorOp.shard,
errorOp.type, errorOp.when, errorOp.duration};
int rc = send_osd_command(osd, injectErrorRequest, rados,
"InjectECErrorRequest", inject_inbl,
} else if (errorOp.type == 1) {
ceph::messaging::osd::InjectECErrorRequest<
InjectOpType::ReadMissingShard>
- injectErrorRequest{pool, oid, errorOp.shard,
+ injectErrorRequest{pool, primary_oid, errorOp.shard,
errorOp.type, errorOp.when, errorOp.duration};
int rc = send_osd_command(osd, injectErrorRequest, rados,
"InjectECErrorRequest", inject_inbl,
if (errorOp.type == 0) {
ceph::messaging::osd::InjectECErrorRequest<
InjectOpType::WriteFailAndRollback>
- injectErrorRequest{pool, oid, errorOp.shard,
+ injectErrorRequest{pool, primary_oid, errorOp.shard,
errorOp.type, errorOp.when, errorOp.duration};
int rc = send_osd_command(osd, injectErrorRequest, rados,
"InjectECErrorRequest", inject_inbl,
ceph_assert(rc == 0);
} else if (errorOp.type == 3) {
ceph::messaging::osd::InjectECErrorRequest<InjectOpType::WriteOSDAbort>
- injectErrorRequest{pool, oid, errorOp.shard,
+ injectErrorRequest{pool, primary_oid, errorOp.shard,
errorOp.type, errorOp.when, errorOp.duration};
int rc = send_osd_command(osd, injectErrorRequest, rados,
"InjectECErrorRequest", inject_inbl,
if (errorOp.type == 0) {
ceph::messaging::osd::InjectECClearErrorRequest<InjectOpType::ReadEIO>
- clearErrorInject{pool, oid, errorOp.shard, errorOp.type};
+ clearErrorInject{pool, primary_oid, errorOp.shard, errorOp.type};
int rc = send_osd_command(osd, clearErrorInject, rados,
"InjectECClearErrorRequest", inject_inbl,
&inject_outbl, formatter.get());
} else if (errorOp.type == 1) {
ceph::messaging::osd::InjectECClearErrorRequest<
InjectOpType::ReadMissingShard>
- clearErrorInject{pool, oid, errorOp.shard, errorOp.type};
+ clearErrorInject{pool, primary_oid, errorOp.shard, errorOp.type};
int rc = send_osd_command(osd, clearErrorInject, rados,
"InjectECClearErrorRequest", inject_inbl,
&inject_outbl, formatter.get());
if (errorOp.type == 0) {
ceph::messaging::osd::InjectECClearErrorRequest<
InjectOpType::WriteFailAndRollback>
- clearErrorInject{pool, oid, errorOp.shard, errorOp.type};
+ clearErrorInject{pool, primary_oid, errorOp.shard, errorOp.type};
int rc = send_osd_command(osd, clearErrorInject, rados,
"InjectECClearErrorRequest", inject_inbl,
&inject_outbl, formatter.get());
} else if (errorOp.type == 3) {
ceph::messaging::osd::InjectECClearErrorRequest<
InjectOpType::WriteOSDAbort>
- clearErrorInject{pool, oid, errorOp.shard, errorOp.type};
+ clearErrorInject{pool, primary_oid, errorOp.shard, errorOp.type};
int rc = send_osd_command(osd, clearErrorInject, rados,
"InjectECClearErrorRequest", inject_inbl,
&inject_outbl, formatter.get());
fmt::format("Unsupported inject operation ({})", op.getOpType()));
break;
}
-}
\ No newline at end of file
+}
public:
RadosIo(librados::Rados& rados, boost::asio::io_context& asio,
- const std::string& pool, const std::string& oid,
+ const std::string& pool, const std::string& primary_oid, const std::string& secondary_oid,
const std::optional<std::vector<int>>& cached_shard_order,
uint64_t block_size, int seed, int threads, ceph::mutex& lock,
ceph::condition_variable& cond, bool is_replicated_pool,
"\t Run parallel test to multiple objects. First object is tested with",
"\t default settings, other objects are tested with random settings",
"",
+ "ceph_test_rados_io_sequence --object_copy <oid>",
+ "\t Specify a second object to be used in copy operations",
+ "",
"Advanced usage:",
"",
"ceph_test_rados_io_sequence --blocksize <b> --km <k,m> --plugin <p>",
"\t are specified with unit of blocksize. Supported commands:",
"\t\t create <len>",
"\t\t remove",
+ "\t\t swap",
+ "\t\t copy",
"\t\t read|write|failedwrite <off> <len>",
"\t\t read2|write2|failedwrite2 <off> <len> <off> <len>",
"\t\t read3|write3|failedwrite3 <off> <len> <off> <len> <off> <len>",
"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")("plugin", po::value<PluginString>(), "EC plugin")(
+ "primary object name")(
+ "object_copy", po::value<std::string>()->default_value("test_copy"), "secondary 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")(
"threads,t", po::value<int>(),
"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")(
+ "number of objects (or object pairs) to exercise in parallel")(
"testrecovery",
"Inject errors during sequences to test recovery processes of OSDs")(
"checkconsistency",
}
ceph::io_sequence::tester::TestObject::TestObject(
- const std::string oid, librados::Rados& rados,
+ const std::string primary_oid, const std::string secondary_oid, librados::Rados& rados,
boost::asio::io_context& asio, SelectBlockSize& sbs, SelectErasurePool& spo,
SelectObjectSize& sos, SelectNumThreads& snt, SelectSeqRange& ssr,
ceph::util::random_number_generator<int>& rng, ceph::mutex& lock,
testrecovery(testrecovery), checkconsistency(checkconsistency) {
if (dryrun) {
exerciser_model = std::make_unique<ceph::io_exerciser::ObjectModel>(
- oid, sbs.select(), rng());
+ primary_oid, secondary_oid, sbs.select(), rng());
} else {
const std::string pool = spo.select();
if (!dryrun) {
if (!spo.get_allow_pool_autoscaling() && !spo.get_allow_pool_balancer() &&
!spo.get_allow_pool_deep_scrubbing() &&
!spo.get_allow_pool_scrubbing()) {
- ceph::messaging::osd::OSDMapRequest osdMapRequest{pool, oid, ""};
- int rc = send_mon_command(osdMapRequest, rados, "OSDMapRequest", inbl,
- &outbl, formatter.get());
- ceph_assert(rc == 0);
+ {
+ ceph::messaging::osd::OSDMapRequest osdMapRequest{pool, primary_oid, ""};
+ int rc = send_mon_command(osdMapRequest, rados, "OSDMapRequest", inbl,
+ &outbl, formatter.get());
+ ceph_assert(rc == 0);
+ }
+ {
+ ceph::messaging::osd::OSDMapRequest osdMapRequest{pool, secondary_oid, ""};
+ int rc = send_mon_command(osdMapRequest, rados, "OSDMapRequest", inbl,
+ &outbl, formatter.get());
+ ceph_assert(rc == 0);
+ }
JSONParser p;
bool success = p.parse(outbl.c_str(), outbl.length());
}
exerciser_model = std::make_unique<ceph::io_exerciser::RadosIo>(
- rados, asio, pool, oid, cached_shard_order, sbs.select(), rng(),
+ rados, asio, pool, primary_oid, secondary_cached_shard_order, sbs.select(), rng(),
threads, lock, cond, spo.is_replicated_pool(),
spo.get_allow_pool_ec_optimizations());
dout(0) << "= " << oid << " pool=" << pool << " threads=" << threads
op = seq->next();
done = false;
- dout(0) << "== " << exerciser_model->get_oid() << " " << curseq << " "
+ dout(0) << "== " << exerciser_model->get_primary_oid() << " " << curseq << " "
<< seq->get_name_with_seqseed() << " ==" << dendl;
}
bool ceph::io_sequence::tester::TestObject::next() {
if (!done) {
if (verbose) {
- dout(0) << exerciser_model->get_oid() << " Step " << seq->get_step()
+ dout(0) << exerciser_model->get_primary_oid() << " Step " << seq->get_step()
<< ": " << op->to_string(exerciser_model->get_block_size())
<< dendl;
} else {
- dout(5) << exerciser_model->get_oid() << " Step " << seq->get_step()
+ dout(5) << exerciser_model->get_primary_oid() << " Step " << seq->get_step()
<< ": " << op->to_string(exerciser_model->get_block_size())
<< dendl;
}
curseq = seq->getNextSupportedSequenceId();
if (curseq >= seq_range.second) {
done = true;
- dout(0) << exerciser_model->get_oid()
+ dout(0) << exerciser_model->get_primary_oid()
<< " Number of IOs = " << exerciser_model->get_num_io()
<< dendl;
} else {
curseq, obj_size_range, seqseed.value_or(rng()), checkconsistency);
}
- dout(0) << "== " << exerciser_model->get_oid() << " " << curseq << " "
+ dout(0) << "== " << exerciser_model->get_primary_oid() << " " << curseq << " "
<< seq->get_name_with_seqseed() << " ==" << dendl;
op = seq->next();
}
if (vm.contains("seqseed")) {
seqseed = vm["seqseed"].as<int>();
}
- num_objects = vm["parallel"].as<int>();
- object_name = vm["object"].as<std::string>();
+ num_object_pairs = vm["parallel"].as<int>();
+ primary_object_name = vm["object"].as<std::string>();
+ secondary_object_name = vm["object_copy"].as<std::string>();
interactive = vm.contains("interactive");
testrecovery = vm.contains("testrecovery");
checkconsistency = vm.contains("checkconsistency");
if (dryrun) {
model = std::make_unique<ceph::io_exerciser::ObjectModel>(
- object_name, sbs.select(), rng());
+ primary_object_name, secondary_object_name, sbs.select(), rng());
} else {
const std::string pool = spo.select();
bufferlist inbl, outbl;
auto formatter = std::make_unique<JSONFormatter>(false);
- ceph::messaging::osd::OSDMapRequest osd_map_request{pool, object_name, ""};
+ {
+ ceph::messaging::osd::OSDMapRequest osd_map_request{pool, primary_object_name, ""};
int rc = send_mon_command(osd_map_request, rados, "OSDMapRequest", inbl,
&outbl, formatter.get());
ceph_assert(rc == 0);
+ }
+ {
+ ceph::messaging::osd::OSDMapRequest osdMapRequest{pool, secondary_object_name, ""};
+ int rc = send_mon_command(osdMapRequest, rados, "OSDMapRequest", inbl,
+ &outbl, formatter.get());
+ ceph_assert(rc == 0);
+ }
JSONParser p;
bool success = p.parse(outbl.c_str(), outbl.length());
osd_map_reply.decode_json(&p);
model = std::make_unique<ceph::io_exerciser::RadosIo>(
- rados, asio, pool, object_name, osd_map_reply.acting, sbs.select(), rng(),
+ rados, asio, pool, primary_object_name, secondary_object_name, osd_map_reply.acting, sbs.select(), rng(),
1, // 1 thread
lock, cond, spo.is_replicated_pool(),
spo.get_allow_pool_ec_optimizations());
uint64_t duration = get_numeric_token();
dout(0) << "Sleep " << duration << dendl;
sleep(duration);
+ } else if (op == "swap") {
+ ioop = ceph::io_exerciser::SwapOp::generate();
+ } else if (op == "copy") {
+ ioop = ceph::io_exerciser::CopyOp::generate();
} else if (op == "create") {
ioop = ceph::io_exerciser::CreateOp::generate(get_numeric_token());
} else if (op == "remove" || op == "delete") {
<< dendl;
}
if (ioop) {
- dout(0) << ioop->to_string(model->get_block_size()) << dendl;
+ dout(0) << model->get_primary_oid() << " " << ioop->to_string(model->get_block_size()) << dendl;
model->applyIoOp(*ioop);
done = ioop->getOpType() == ceph::io_exerciser::OpType::Done;
if (!done) {
std::vector<std::shared_ptr<ceph::io_sequence::tester::TestObject>>
test_objects;
- for (int obj = 0; obj < num_objects; obj++) {
- std::string name;
+ for (int obj = 0; obj < num_object_pairs; obj++) {
+ std::string primary_name;
+ std::string secondary_name;
if (obj == 0) {
- name = object_name;
+ primary_name = primary_object_name;
+ secondary_name = secondary_object_name;
} else {
- name = object_name + std::to_string(obj);
+ primary_name = primary_object_name + std::to_string(obj);
+ secondary_name = secondary_object_name + std::to_string(obj);
}
try {
test_objects.push_back(
std::make_shared<ceph::io_sequence::tester::TestObject>(
- name, rados, asio, sbs, spo, sos, snt, ssr, rng, lock, cond,
+ primary_name, secondary_name, rados, asio, sbs, spo, sos, snt, ssr, rng, lock, cond,
dryrun, verbose, seqseed, testrecovery, checkconsistency));
}
catch (const std::runtime_error &e) {
class TestObject {
public:
- TestObject(const std::string oid,
+ TestObject(const std::string primary_oid,
+ const std::string secondary_oid,
librados::Rados& rados,
boost::asio::io_context& asio,
ceph::io_sequence::tester::SelectBlockSize& sbs,
bool show_sequence;
bool show_help;
- int num_objects;
- std::string object_name;
+ int num_object_pairs;
+ std::string primary_object_name;
+ std::string secondary_object_name;
std::string line;
ceph::split split = ceph::split("");