+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 20 << 20; /* 20MiB */
+ off_t off = 512;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ std::string cmp_buffer("This is a test");
+ ceph::bufferlist cmp_bl;
+ cmp_bl.append(&cmp_buffer[0], 5);
+ cmp_bl.append(&cmp_buffer[5], 3);
+ cmp_bl.append(&cmp_buffer[8], 2);
+ cmp_bl.append(&cmp_buffer[10], 4);
+
+ ssize_t written = image.write(off, cmp_bl.length(), cmp_bl);
+ ASSERT_EQ(cmp_bl.length(), written);
+
+ std::string small_buffer("Too small");
+ ceph::bufferlist small_bl;
+ small_bl.append(&small_buffer[0], 4);
+ small_bl.append(&small_buffer[4], 4);
+
+ // should fail because write bufferlist cannot be smaller than len
+ librbd::RBD::AioCompletion *comp = new librbd::RBD::AioCompletion(
+ NULL, (librbd::callback_t) simple_write_cb_pp);
+ uint64_t mismatch_off = 0;
+ int ret = image.aio_compare_and_write(off, cmp_bl.length(),
+ cmp_bl,
+ small_bl, /* write_bl */
+ comp, &mismatch_off, 0);
+ ASSERT_EQ(-EINVAL, ret);
+ ASSERT_EQ(0U, mismatch_off);
+ comp->release();
+
+ ASSERT_PASSED(validate_object_map, image);
+ }
+
+ ioctx.close();
+}
+
+TEST_F(TestLibRBD, TestCompareAndWriteMismatchPP)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 20 << 20; /* 20MiB */
+ off_t off = 512;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ std::string cmp_buffer("This is a test");
+ ceph::bufferlist cmp_bl;
+ cmp_bl.append(&cmp_buffer[0], 5);
+ cmp_bl.append(&cmp_buffer[5], 3);
+ cmp_bl.append(&cmp_buffer[8], 2);
+ cmp_bl.append(&cmp_buffer[10], 4);
+
+ std::string write_buffer("Write this !!!");
+ ceph::bufferlist write_bl;
+ write_bl.append(&write_buffer[0], 6);
+ write_bl.append(&write_buffer[6], 5);
+ write_bl.append(&write_buffer[11], 3);
+
+ std::string mismatch_buffer("This will fail");
+ ceph::bufferlist mismatch_bl;
+ mismatch_bl.append(&mismatch_buffer[0], 5);
+ mismatch_bl.append(&mismatch_buffer[5], 5);
+ mismatch_bl.append(&mismatch_buffer[10], 4);
+
+ ssize_t written = image.write(off, cmp_bl.length(), cmp_bl);
+ ASSERT_EQ(cmp_bl.length(), written);
+
+ // this should execute the compare but fail because of mismatch
+ uint64_t mismatch_off = 0;
+ written = image.compare_and_write(off, write_bl.length(),
+ mismatch_bl, /* cmp_bl */
+ write_bl,
+ &mismatch_off, 0);
+ ASSERT_EQ(-EILSEQ, written);
+ ASSERT_EQ(5U, mismatch_off);
+
+ // check that nothing was written
+ ASSERT_PASSED(compare_written, image, off, cmp_bl.length(), cmp_buffer);
+
+ ASSERT_PASSED(validate_object_map, image);
+ }
+
+ ioctx.close();
+}
+
+TEST_F(TestLibRBD, TestAioCompareAndWriteMismatchPP)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 20 << 20; /* 20MiB */
+ off_t off = 512;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ std::string cmp_buffer("This is a test");
+ ceph::bufferlist cmp_bl;
+ cmp_bl.append(&cmp_buffer[0], 5);
+ cmp_bl.append(&cmp_buffer[5], 3);
+ cmp_bl.append(&cmp_buffer[8], 2);
+ cmp_bl.append(&cmp_buffer[10], 4);
+
+ std::string write_buffer("Write this !!!");
+ ceph::bufferlist write_bl;
+ write_bl.append(&write_buffer[0], 6);
+ write_bl.append(&write_buffer[6], 5);
+ write_bl.append(&write_buffer[11], 3);
+
+ std::string mismatch_buffer("This will fail");
+ ceph::bufferlist mismatch_bl;
+ mismatch_bl.append(&mismatch_buffer[0], 5);
+ mismatch_bl.append(&mismatch_buffer[5], 5);
+ mismatch_bl.append(&mismatch_buffer[10], 4);
+
+ ssize_t written = image.write(off, cmp_bl.length(), cmp_bl);
+ ASSERT_EQ(cmp_bl.length(), written);
+
+ // this should execute the compare but fail because of mismatch
+ librbd::RBD::AioCompletion *comp = new librbd::RBD::AioCompletion(
+ NULL, (librbd::callback_t) simple_write_cb_pp);
+ uint64_t mismatch_off = 0;
+ int ret = image.aio_compare_and_write(off, write_bl.length(),
+ mismatch_bl, /* cmp_bl */
+ write_bl,
+ comp, &mismatch_off, 0);
+ ASSERT_EQ(0, ret);
+ comp->wait_for_complete();
+ ssize_t aio_ret = comp->get_return_value();
+ ASSERT_EQ(-EILSEQ, aio_ret);
+ ASSERT_EQ(5U, mismatch_off);
+ comp->release();
+
+ // check that nothing was written
+ ASSERT_PASSED(compare_written, image, off, cmp_bl.length(), cmp_buffer);
+
+ ASSERT_PASSED(validate_object_map, image);
+ }
+
+ ioctx.close();
+}
+
+TEST_F(TestLibRBD, TestCompareAndWriteMismatchBufferlistGreaterLenPP)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 20 << 20; /* 20MiB */
+ off_t off = 512;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ std::string cmp_buffer("This is a test");
+ ceph::bufferlist cmp_bl;
+ cmp_bl.append(&cmp_buffer[0], 5);
+ cmp_bl.append(&cmp_buffer[5], 3);
+ cmp_bl.append(&cmp_buffer[8], 2);
+ cmp_bl.append(&cmp_buffer[10], 4);
+
+ std::string write_buffer("Write this !!!");
+ ceph::bufferlist write_bl;
+ write_bl.append(&write_buffer[0], 6);
+ write_bl.append(&write_buffer[6], 5);
+ write_bl.append(&write_buffer[11], 3);
+
+ std::string mismatch_buffer("This will fail");
+ ceph::bufferlist mismatch_bl;
+ mismatch_bl.append(&mismatch_buffer[0], 5);
+ mismatch_bl.append(&mismatch_buffer[5], 5);
+ mismatch_bl.append(&mismatch_buffer[10], 4);
+
+ ssize_t written = image.write(off, cmp_bl.length(), cmp_bl);
+ ASSERT_EQ(cmp_bl.length(), written);
+
+ /*
+ * we allow cmp_bl and write_bl to be greater than len so this
+ * should execute the compare but fail because of mismatch
+ */
+ uint64_t mismatch_off = 0;
+ written = image.compare_and_write(off, cmp_bl.length() - 1,
+ mismatch_bl, /* cmp_bl */
+ write_bl,
+ &mismatch_off, 0);
+ ASSERT_EQ(-EILSEQ, written);
+ ASSERT_EQ(5U, mismatch_off);
+
+ // check that nothing was written
+ ASSERT_PASSED(compare_written, image, off, cmp_bl.length(), cmp_buffer);
+
+ ASSERT_PASSED(validate_object_map, image);
+ }
+
+ ioctx.close();
+}
+
+TEST_F(TestLibRBD, TestAioCompareAndWriteMismatchBufferlistGreaterLenPP)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 20 << 20; /* 20MiB */
+ off_t off = 512;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ std::string cmp_buffer("This is a test");
+ ceph::bufferlist cmp_bl;
+ cmp_bl.append(&cmp_buffer[0], 5);
+ cmp_bl.append(&cmp_buffer[5], 3);
+ cmp_bl.append(&cmp_buffer[8], 2);
+ cmp_bl.append(&cmp_buffer[10], 4);
+
+ std::string write_buffer("Write this !!!");
+ ceph::bufferlist write_bl;
+ write_bl.append(&write_buffer[0], 6);
+ write_bl.append(&write_buffer[6], 5);
+ write_bl.append(&write_buffer[11], 3);
+
+ std::string mismatch_buffer("This will fail");
+ ceph::bufferlist mismatch_bl;
+ mismatch_bl.append(&mismatch_buffer[0], 5);
+ mismatch_bl.append(&mismatch_buffer[5], 5);
+ mismatch_bl.append(&mismatch_buffer[10], 4);
+
+ ssize_t written = image.write(off, cmp_bl.length(), cmp_bl);
+ ASSERT_EQ(cmp_bl.length(), written);
+
+ /*
+ * we allow cmp_bl and write_bl to be greater than len so this
+ * should execute the compare but fail because of mismatch
+ */
+ librbd::RBD::AioCompletion *comp = new librbd::RBD::AioCompletion(
+ NULL, (librbd::callback_t) simple_write_cb_pp);
+ uint64_t mismatch_off = 0;
+ int ret = image.aio_compare_and_write(off, cmp_bl.length() - 1,
+ mismatch_bl, /* cmp_bl */
+ write_bl,
+ comp, &mismatch_off, 0);
+ ASSERT_EQ(0, ret);
+ comp->wait_for_complete();
+ ssize_t aio_ret = comp->get_return_value();
+ ASSERT_EQ(-EILSEQ, aio_ret);
+ ASSERT_EQ(5U, mismatch_off);
+ comp->release();
+
+ // check that nothing was written
+ ASSERT_PASSED(compare_written, image, off, cmp_bl.length(), cmp_buffer);
+
+ ASSERT_PASSED(validate_object_map, image);
+ }
+
+ ioctx.close();
+}
+
+TEST_F(TestLibRBD, TestCompareAndWriteSuccessPP)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 20 << 20; /* 20MiB */
+ off_t off = 512;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ std::string cmp_buffer("This is a test");
+ ceph::bufferlist cmp_bl;
+ cmp_bl.append(&cmp_buffer[0], 5);
+ cmp_bl.append(&cmp_buffer[5], 3);
+ cmp_bl.append(&cmp_buffer[8], 2);
+ cmp_bl.append(&cmp_buffer[10], 4);
+
+ std::string write_buffer("Write this !!!");
+ ceph::bufferlist write_bl;
+ write_bl.append(&write_buffer[0], 6);
+ write_bl.append(&write_buffer[6], 5);
+ write_bl.append(&write_buffer[11], 3);
+
+ ssize_t written = image.write(off, cmp_bl.length(), cmp_bl);
+ ASSERT_EQ(cmp_bl.length(), written);
+
+ // compare against the buffer written before => should succeed
+ uint64_t mismatch_off = 0;
+ written = image.compare_and_write(off, cmp_bl.length(),
+ cmp_bl,
+ write_bl,
+ &mismatch_off, 0);
+ ASSERT_EQ(write_bl.length(), written);
+ ASSERT_EQ(0U, mismatch_off);
+
+ // check write_bl was written
+ ASSERT_PASSED(compare_written, image, off, write_bl.length(), write_buffer);
+
+ ASSERT_PASSED(validate_object_map, image);
+ }
+
+ ioctx.close();
+}
+
+TEST_F(TestLibRBD, TestAioCompareAndWriteSuccessPP)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 20 << 20; /* 20MiB */
+ off_t off = 512;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ std::string cmp_buffer("This is a test");
+ ceph::bufferlist cmp_bl;
+ cmp_bl.append(&cmp_buffer[0], 5);
+ cmp_bl.append(&cmp_buffer[5], 3);
+ cmp_bl.append(&cmp_buffer[8], 2);
+ cmp_bl.append(&cmp_buffer[10], 4);
+
+ std::string write_buffer("Write this !!!");
+ ceph::bufferlist write_bl;
+ write_bl.append(&write_buffer[0], 6);
+ write_bl.append(&write_buffer[6], 5);
+ write_bl.append(&write_buffer[11], 3);
+
+ ssize_t written = image.write(off, cmp_bl.length(), cmp_bl);
+ ASSERT_EQ(cmp_bl.length(), written);
+
+ // compare against the buffer written before => should succeed
+ librbd::RBD::AioCompletion *comp = new librbd::RBD::AioCompletion(
+ NULL, (librbd::callback_t) simple_write_cb_pp);
+ uint64_t mismatch_off = 0;
+ int ret = image.aio_compare_and_write(off, write_bl.length(),
+ cmp_bl,
+ write_bl,
+ comp, &mismatch_off, 0);
+ ASSERT_EQ(0, ret);
+ comp->wait_for_complete();
+ ssize_t aio_ret = comp->get_return_value();
+ ASSERT_EQ(0, aio_ret);
+ ASSERT_EQ(0U, mismatch_off);
+ comp->release();
+
+ // check write_bl was written
+ ASSERT_PASSED(compare_written, image, off, write_bl.length(), write_buffer);
+
+ ASSERT_PASSED(validate_object_map, image);
+ }
+
+ ioctx.close();
+}
+
+TEST_F(TestLibRBD, TestCompareAndWriteSuccessBufferlistGreaterLenPP)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 20 << 20; /* 20MiB */
+ off_t off = 512;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ std::string cmp_buffer("This is a test");
+ ceph::bufferlist cmp_bl;
+ cmp_bl.append(&cmp_buffer[0], 5);
+ cmp_bl.append(&cmp_buffer[5], 3);
+ cmp_bl.append(&cmp_buffer[8], 2);
+ cmp_bl.append(&cmp_buffer[10], 4);
+
+ std::string write_buffer("Write this !!!");
+ ceph::bufferlist write_bl;
+ write_bl.append(&write_buffer[0], 6);
+ write_bl.append(&write_buffer[6], 5);
+ write_bl.append(&write_buffer[11], 3);
+
+ std::string mismatch_buffer("This will fail");
+ ceph::bufferlist mismatch_bl;
+ mismatch_bl.append(&mismatch_buffer[0], 5);
+ mismatch_bl.append(&mismatch_buffer[5], 5);
+ mismatch_bl.append(&mismatch_buffer[10], 4);
+
+ /*
+ * Test len < cmp_bl & write_bl => should succeed but only compare
+ * len bytes resp. only write len bytes
+ */
+ ssize_t written = image.write(off, mismatch_bl.length(), mismatch_bl);
+ ASSERT_EQ(mismatch_bl.length(), written);
+
+ size_t len_m1 = cmp_bl.length() - 1;
+ written = image.write(off, len_m1, cmp_bl);
+ ASSERT_EQ(len_m1, written);
+ // the content of the image at off should now be "This is a tesl"
+
+ uint64_t mismatch_off = 0;
+ written = image.compare_and_write(off, len_m1,
+ cmp_bl,
+ write_bl,
+ &mismatch_off, 0);
+ ASSERT_EQ(len_m1, written);
+ ASSERT_EQ(0U, mismatch_off);
+
+ // check that only write_bl.length() - 1 bytes were written
+ ASSERT_PASSED(compare_written, image, off, len_m1, write_buffer);
+ ASSERT_PASSED(compare_written, image, off + len_m1, 1, std::string("l"));
+
+ ASSERT_PASSED(validate_object_map, image);
+ }
+
+ ioctx.close();
+}
+
+TEST_F(TestLibRBD, TestAioCompareAndWriteSuccessBufferlistGreaterLenPP)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 20 << 20; /* 20MiB */
+ off_t off = 512;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ std::string cmp_buffer("This is a test");
+ ceph::bufferlist cmp_bl;
+ cmp_bl.append(&cmp_buffer[0], 5);
+ cmp_bl.append(&cmp_buffer[5], 3);
+ cmp_bl.append(&cmp_buffer[8], 2);
+ cmp_bl.append(&cmp_buffer[10], 4);
+
+ std::string write_buffer("Write this !!!");
+ ceph::bufferlist write_bl;
+ write_bl.append(&write_buffer[0], 6);
+ write_bl.append(&write_buffer[6], 5);
+ write_bl.append(&write_buffer[11], 3);
+
+ std::string mismatch_buffer("This will fail");
+ ceph::bufferlist mismatch_bl;
+ mismatch_bl.append(&mismatch_buffer[0], 5);
+ mismatch_bl.append(&mismatch_buffer[5], 5);
+ mismatch_bl.append(&mismatch_buffer[10], 4);
+
+ ssize_t written = image.write(off, cmp_bl.length(), cmp_bl);
+ ASSERT_EQ(cmp_bl.length(), written);
+
+ /*
+ * Test len < cmp_bl & write_bl => should succeed but only compare
+ * len bytes resp. only write len bytes
+ */
+ written = image.write(off, mismatch_bl.length(), mismatch_bl);
+ ASSERT_EQ(mismatch_bl.length(), written);
+
+ size_t len_m1 = cmp_bl.length() - 1;
+ written = image.write(off, len_m1, cmp_bl);
+ ASSERT_EQ(len_m1, written);
+ // the content of the image at off should now be "This is a tesl"
+
+ librbd::RBD::AioCompletion *comp = new librbd::RBD::AioCompletion(
+ NULL, (librbd::callback_t) simple_write_cb_pp);
+ uint64_t mismatch_off = 0;
+ int ret = image.aio_compare_and_write(off, len_m1,
+ cmp_bl,
+ write_bl,
+ comp, &mismatch_off, 0);
+ ASSERT_EQ(0, ret);
+ comp->wait_for_complete();
+ ssize_t aio_ret = comp->get_return_value();
+ ASSERT_EQ(0, aio_ret);
+ ASSERT_EQ(0U, mismatch_off);