]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
test/osd: Extend ceph_test_rados_io_sequence to support copy operations
authorAdam Lyon-Jones <adamlyon@uk.ibm.com>
Tue, 10 Jun 2025 08:12:47 +0000 (09:12 +0100)
committerAdam Lyon-Jones <adamlyon@uk.ibm.com>
Tue, 9 Sep 2025 08:30:07 +0000 (09:30 +0100)
- Adds support for the I/O exerciser to handle sequences involving pairs of
objects, including a swap operation to tell it which object to treat as
active. This is supported in interactive mode.
- Adds an operation to copy data from one object to another using
copy-from. This is supported in interactive mode.
- Adds support for using the truncate operation to make an object bigger.
- Adds a new CLI argument (--object_copy) to specify a secondary object to use.
- Adds a new sequence (SEQUENCE15) to test copying from one object to another.

Signed-off-by: Adam Lyon-Jones <adamlyon@uk.ibm.com>
14 files changed:
src/common/io_exerciser/DataGenerator.cc
src/common/io_exerciser/IoOp.cc
src/common/io_exerciser/IoOp.h
src/common/io_exerciser/IoSequence.cc
src/common/io_exerciser/IoSequence.h
src/common/io_exerciser/Model.cc
src/common/io_exerciser/Model.h
src/common/io_exerciser/ObjectModel.cc
src/common/io_exerciser/ObjectModel.h
src/common/io_exerciser/OpType.h
src/common/io_exerciser/RadosIo.cc
src/common/io_exerciser/RadosIo.h
src/test/osd/ceph_test_rados_io_sequence/ceph_test_rados_io_sequence.cc
src/test/osd/ceph_test_rados_io_sequence/ceph_test_rados_io_sequence.h

index 573c38714b1c134b579f01ae5ae666b4f5ec6e70..45fc5f60b2f1eca02651e6f12043d8a8d9c4fac8 100644 (file)
@@ -226,7 +226,7 @@ bool HeaderedSeededRandomGenerator::validate(bufferlist& bufferlist,
   }
 
   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);
   }
index 4b9fc57ce39879c78dfaf395df31d7e77c919123..4bb6fb761eb07162bb041e97ad762cc09882ccd4 100644 (file)
@@ -11,6 +11,8 @@ using BarrierOp = ceph::io_exerciser::BarrierOp;
 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;
@@ -76,6 +78,22 @@ std::unique_ptr<RemoveOp> RemoveOp::generate() {
 
 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,
index ae58f6c40cd5099d79271bb992b3fa0a4469c2ae..88165db8180b5d65b1e713e32125a750b956daeb 100644 (file)
@@ -68,6 +68,20 @@ class ConsistencyOp : public TestOp<OpType::Consistency> {
    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:
@@ -266,4 +280,4 @@ class ClearWriteErrorInjectOp
   }
 };
 }  // namespace io_exerciser
-}  // namespace ceph
\ No newline at end of file
+}  // namespace ceph
index f855aea3e42810eda64f85f4e43a97d2a99904dc..2634b9a38a7667689480292221b320cfb7da7b74 100644 (file)
@@ -56,6 +56,9 @@ std::ostream& ceph::io_exerciser::operator<<(std::ostream& os,
     case Sequence::SEQUENCE_SEQ14:
       os << "SEQUENCE_SEQ14";
       break;
+    case Sequence::SEQUENCE_SEQ15:
+      os << "SEQUENCE_SEQ15";
+      break;
     case Sequence::SEQUENCE_END:
       os << "SEQUENCE_END";
       break;
@@ -103,6 +106,8 @@ std::unique_ptr<IoSequence> IoSequence::generate_sequence(
       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;
   }
@@ -120,6 +125,7 @@ IoSequence::IoSequence(std::pair<int, int> obj_size_range, int seed, bool check_
       consistency_in_progress(false),
       consistency_request_sent(false),
       check_consistency(check_consistency),
+      swap(false),
       obj_size(min_obj_size),
       step(-1),
       seed(seed) {
@@ -216,6 +222,10 @@ std::unique_ptr<IoOp> IoSequence::next() {
   if (done) {
     return DoneOp::generate();
   }
+  if (swap) {
+    swap = false;
+    return SwapOp::generate();
+  }
   if (create) {
     create = false;
     barrier = true;
@@ -745,3 +755,108 @@ std::unique_ptr<ceph::io_exerciser::IoOp> ceph::io_exerciser::Seq14::_next() {
   }
   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;
+}
index 86282cabcc1f71936481760f8c8cff92494f3429..bbf564fcdd7a6926324ef15c7fae29b36b5d88b4 100644 (file)
@@ -46,6 +46,7 @@ enum class Sequence {
   SEQUENCE_SEQ12,
   SEQUENCE_SEQ13,
   SEQUENCE_SEQ14,
+  SEQUENCE_SEQ15,
 
   SEQUENCE_END,
   SEQUENCE_BEGIN = SEQUENCE_SEQ0
@@ -87,6 +88,7 @@ class IoSequence {
   bool consistency_in_progress;
   bool consistency_request_sent;
   bool check_consistency;
+  bool swap;
   uint64_t obj_size;
   int step;
   int seed;
@@ -305,5 +307,34 @@ class Seq14 : public IoSequence {
   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
index 6548e1eda7a80e59521c1a70e6aadeb1b80aa90e..536c406539ab1d4df68d21bd3f398efec932f210 100644 (file)
@@ -4,11 +4,28 @@
 
 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
index 5598378fe11708538b9115104fd04bb8eba58b39..dc9cb50e22e13e1017132d940bfb5acec262e717 100644 (file)
@@ -29,17 +29,23 @@ class IoOp;
 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;
 };
index b4edf7925e2d0cb58559b9716fa86983e98f1d3a..6cd682f4eb5860bf7fed79ef31462119c5b3d087 100644 (file)
@@ -7,20 +7,20 @@
 
 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);
     }
   }
@@ -29,15 +29,15 @@ std::vector<int> ObjectModel::get_seed_offsets(int seed) const {
 }
 
 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;
@@ -51,12 +51,14 @@ void ObjectModel::applyIoOp(IoOp& op) {
   };
 
   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]);
@@ -65,21 +67,23 @@ void ObjectModel::applyIoOp(IoOp& op) {
       };
 
   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);
         }
@@ -87,18 +91,20 @@ void ObjectModel::applyIoOp(IoOp& op) {
       };
 
   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++;
       };
@@ -109,31 +115,58 @@ void ObjectModel::applyIoOp(IoOp& op) {
       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);
@@ -148,37 +181,40 @@ void ObjectModel::applyIoOp(IoOp& op) {
     } 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;
@@ -189,9 +225,9 @@ void ObjectModel::applyIoOp(IoOp& op) {
 
 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);
 }
@@ -199,11 +235,9 @@ void ObjectModel::encode(ceph::buffer::list& bl) const {
 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);
 }
index 43d2188137479db4cd0986865fb4cad2b5999131..5bca4ea04f9afdaa33ccec7f1385cd739a379238 100644 (file)
@@ -25,8 +25,10 @@ namespace io_exerciser {
 
 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>();
 
@@ -43,7 +45,7 @@ class ObjectModel : public Model {
   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;
index 3c7f535839c31aad309b498438a4f11db9c89f60..af939614570ea1dc174698c7e4f9d2520a33c654 100644 (file)
@@ -18,6 +18,7 @@ enum class OpType {
   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
@@ -29,6 +30,7 @@ enum class OpType {
   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
@@ -62,6 +64,10 @@ struct fmt::formatter<ceph::io_exerciser::OpType> {
         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:
index 468b454273e196eb42558bae0ffc2d3303685ac3..90fc8c217c9e1e4f964bb9262619e5e35263595a 100644 (file)
@@ -43,15 +43,15 @@ int send_mon_command(S& s, librados::Rados& rados, const char* name,
 }  // 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),
@@ -125,6 +125,10 @@ void RadosIo::applyIoOp(IoOp& op) {
       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;
@@ -138,7 +142,7 @@ void RadosIo::applyIoOp(IoOp& op) {
         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;
     }
@@ -153,7 +157,7 @@ void RadosIo::applyIoOp(IoOp& op) {
         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;
     }
@@ -167,7 +171,7 @@ void RadosIo::applyIoOp(IoOp& op) {
         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;
     }
@@ -176,7 +180,7 @@ void RadosIo::applyIoOp(IoOp& op) {
       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);
@@ -184,6 +188,19 @@ void RadosIo::applyIoOp(IoOp& op) {
       }
       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;
     }
 
@@ -244,7 +261,7 @@ void RadosIo::applyReadWriteOp(IoOp& op) {
       }
       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++;
   };
@@ -264,7 +281,7 @@ void RadosIo::applyReadWriteOp(IoOp& op) {
       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++;
   };
@@ -285,7 +302,7 @@ void RadosIo::applyReadWriteOp(IoOp& op) {
       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++;
   };
@@ -368,7 +385,7 @@ void RadosIo::applyInjectOp(IoOp& op) {
   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);
@@ -389,7 +406,7 @@ void RadosIo::applyInjectOp(IoOp& op) {
 
       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,
@@ -398,7 +415,7 @@ void RadosIo::applyInjectOp(IoOp& op) {
       } 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,
@@ -415,7 +432,7 @@ void RadosIo::applyInjectOp(IoOp& op) {
       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,
@@ -423,7 +440,7 @@ void RadosIo::applyInjectOp(IoOp& op) {
         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,
@@ -445,7 +462,7 @@ void RadosIo::applyInjectOp(IoOp& op) {
 
       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());
@@ -453,7 +470,7 @@ void RadosIo::applyInjectOp(IoOp& op) {
       } 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());
@@ -471,7 +488,7 @@ void RadosIo::applyInjectOp(IoOp& op) {
       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());
@@ -479,7 +496,7 @@ void RadosIo::applyInjectOp(IoOp& op) {
       } 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());
@@ -495,4 +512,4 @@ void RadosIo::applyInjectOp(IoOp& op) {
           fmt::format("Unsupported inject operation ({})", op.getOpType()));
       break;
   }
-}
\ No newline at end of file
+}
index ef43ad4317d8241a2a2efc4830e9fffa956d6e96..d1464f3545cf5efd8d45bb9a8a0a55a2fa3f1eb8 100644 (file)
@@ -45,7 +45,7 @@ class RadosIo : public Model {
 
  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,
index 2d31bf872b602741e0feb59d0a08f04b5d802ba8..42348343893419bcef28db41ad47acac33fba716 100644 (file)
@@ -127,6 +127,9 @@ constexpr std::string_view usage[] = {
     "\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>",
@@ -158,6 +161,8 @@ constexpr std::string_view usage[] = {
     "\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>",
@@ -181,7 +186,9 @@ po::options_description get_options_description() {
       "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")(
@@ -195,7 +202,7 @@ po::options_description get_options_description() {
       "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",
@@ -1011,7 +1018,7 @@ void ceph::io_sequence::tester::SelectErasurePool::configureServices(
 }
 
 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,
@@ -1021,7 +1028,7 @@ ceph::io_sequence::tester::TestObject::TestObject(
       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) {
@@ -1045,10 +1052,18 @@ ceph::io_sequence::tester::TestObject::TestObject(
     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());
@@ -1060,7 +1075,7 @@ ceph::io_sequence::tester::TestObject::TestObject(
     }
 
     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
@@ -1082,7 +1097,7 @@ ceph::io_sequence::tester::TestObject::TestObject(
 
   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;
 }
 
@@ -1093,11 +1108,11 @@ bool ceph::io_sequence::tester::TestObject::readyForIo() {
 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;
     }
@@ -1106,7 +1121,7 @@ bool ceph::io_sequence::tester::TestObject::next() {
       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 {
@@ -1119,7 +1134,7 @@ bool ceph::io_sequence::tester::TestObject::next() {
               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();
       }
@@ -1168,8 +1183,9 @@ ceph::io_sequence::tester::TestRunner::TestRunner(
   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");
@@ -1320,17 +1336,25 @@ bool ceph::io_sequence::tester::TestRunner::run_interactive_test() {
 
   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());
@@ -1340,7 +1364,7 @@ bool ceph::io_sequence::tester::TestRunner::run_interactive_test() {
     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());
@@ -1354,6 +1378,10 @@ bool ceph::io_sequence::tester::TestRunner::run_interactive_test() {
       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") {
@@ -1463,7 +1491,7 @@ bool ceph::io_sequence::tester::TestRunner::run_interactive_test() {
               << 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) {
@@ -1481,17 +1509,20 @@ bool ceph::io_sequence::tester::TestRunner::run_automated_test() {
   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) {
index a874cfa924a6f71cb6f78d0c89511219acc58ce0..3d8f5e71e184edff95f55b35069bc6a92a9fc0e1 100644 (file)
@@ -444,7 +444,8 @@ class SelectErasurePool : public ProgramOptionReader<std::string> {
 
 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,
@@ -531,8 +532,9 @@ class TestRunner {
   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("");