diff options
Diffstat (limited to 'src/test/librbd/test_Migration.cc')
-rw-r--r-- | src/test/librbd/test_Migration.cc | 1359 |
1 files changed, 1359 insertions, 0 deletions
diff --git a/src/test/librbd/test_Migration.cc b/src/test/librbd/test_Migration.cc new file mode 100644 index 000000000..8c0f4b61b --- /dev/null +++ b/src/test/librbd/test_Migration.cc @@ -0,0 +1,1359 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "test/librados/test.h" +#include "test/librbd/test_fixture.h" +#include "test/librbd/test_support.h" +#include "librbd/ImageState.h" +#include "librbd/Operations.h" +#include "librbd/api/Group.h" +#include "librbd/api/Image.h" +#include "librbd/api/Io.h" +#include "librbd/api/Migration.h" +#include "librbd/api/Mirror.h" +#include "librbd/api/Namespace.h" +#include "librbd/api/Snapshot.h" +#include "librbd/image/AttachChildRequest.h" +#include "librbd/image/AttachParentRequest.h" +#include "librbd/internal.h" +#include "librbd/io/ReadResult.h" +#include "common/Cond.h" +#include <boost/scope_exit.hpp> + +void register_test_migration() { +} + +namespace librbd { + +struct TestMigration : public TestFixture { + static void SetUpTestCase() { + TestFixture::SetUpTestCase(); + + _other_pool_name = get_temp_pool_name("test-librbd-"); + ASSERT_EQ(0, _rados.pool_create(_other_pool_name.c_str())); + } + + static void TearDownTestCase() { + ASSERT_EQ(0, _rados.pool_delete(_other_pool_name.c_str())); + + TestFixture::TearDownTestCase(); + } + + void SetUp() override { + TestFixture::SetUp(); + + ASSERT_EQ(0, _rados.ioctx_create(_other_pool_name.c_str(), + _other_pool_ioctx)); + + open_image(m_ioctx, m_image_name, &m_ictx); + m_image_id = m_ictx->id; + + std::string ref_image_name = get_temp_image_name(); + ASSERT_EQ(0, create_image_pp(m_rbd, m_ioctx, ref_image_name, m_ictx->size)); + EXPECT_EQ(0, _rados.ioctx_create2(m_ioctx.get_id(), m_ref_ioctx)); + open_image(m_ref_ioctx, ref_image_name, &m_ref_ictx); + + resize(20 * (1 << 22)); + } + + void TearDown() override { + if (m_ref_ictx != nullptr) { + close_image(m_ref_ictx); + } + if (m_ictx != nullptr) { + close_image(m_ictx); + } + + _other_pool_ioctx.close(); + + TestFixture::TearDown(); + } + + void compare(const std::string &description = "") { + std::vector<librbd::snap_info_t> src_snaps, dst_snaps; + + EXPECT_EQ(m_ref_ictx->size, m_ictx->size); + EXPECT_EQ(0, librbd::api::Snapshot<>::list(m_ref_ictx, src_snaps)); + EXPECT_EQ(0, librbd::api::Snapshot<>::list(m_ictx, dst_snaps)); + EXPECT_EQ(src_snaps.size(), dst_snaps.size()); + for (size_t i = 0; i <= src_snaps.size(); i++) { + const char *src_snap_name = nullptr; + const char *dst_snap_name = nullptr; + if (i < src_snaps.size()) { + EXPECT_EQ(src_snaps[i].name, dst_snaps[i].name); + src_snap_name = src_snaps[i].name.c_str(); + dst_snap_name = dst_snaps[i].name.c_str(); + } + EXPECT_EQ(0, librbd::api::Image<>::snap_set( + m_ref_ictx, cls::rbd::UserSnapshotNamespace(), + src_snap_name)); + EXPECT_EQ(0, librbd::api::Image<>::snap_set( + m_ictx, cls::rbd::UserSnapshotNamespace(), + dst_snap_name)); + compare_snaps( + description + " snap: " + (src_snap_name ? src_snap_name : "null"), + m_ref_ictx, m_ictx); + } + } + + void compare_snaps(const std::string &description, librbd::ImageCtx *src_ictx, + librbd::ImageCtx *dst_ictx) { + uint64_t src_size, dst_size; + { + std::shared_lock src_locker{src_ictx->image_lock}; + std::shared_lock dst_locker{dst_ictx->image_lock}; + src_size = src_ictx->get_image_size(src_ictx->snap_id); + dst_size = dst_ictx->get_image_size(dst_ictx->snap_id); + } + if (src_size != dst_size) { + std::cout << description << ": size differs" << std::endl; + EXPECT_EQ(src_size, dst_size); + } + + if (dst_ictx->test_features(RBD_FEATURE_LAYERING)) { + bool flags_set; + std::shared_lock dst_locker{dst_ictx->image_lock}; + EXPECT_EQ(0, dst_ictx->test_flags(dst_ictx->snap_id, + RBD_FLAG_OBJECT_MAP_INVALID, + dst_ictx->image_lock, &flags_set)); + EXPECT_FALSE(flags_set); + } + + ssize_t read_size = 1 << src_ictx->order; + uint64_t offset = 0; + while (offset < src_size) { + read_size = std::min(read_size, static_cast<ssize_t>(src_size - offset)); + + bufferptr src_ptr(read_size); + bufferlist src_bl; + src_bl.push_back(src_ptr); + librbd::io::ReadResult src_result{&src_bl}; + EXPECT_EQ(read_size, api::Io<>::read( + *src_ictx, offset, read_size, + librbd::io::ReadResult{src_result}, 0)); + + bufferptr dst_ptr(read_size); + bufferlist dst_bl; + dst_bl.push_back(dst_ptr); + librbd::io::ReadResult dst_result{&dst_bl}; + EXPECT_EQ(read_size, api::Io<>::read( + *dst_ictx, offset, read_size, + librbd::io::ReadResult{dst_result}, 0)); + + if (!src_bl.contents_equal(dst_bl)) { + std::cout << description + << ", block " << offset << "~" << read_size << " differs" + << std::endl; + std::cout << "src block: " << src_ictx->id << ": " << std::endl; src_bl.hexdump(std::cout); + std::cout << "dst block: " << dst_ictx->id << ": " << std::endl; dst_bl.hexdump(std::cout); + } + EXPECT_TRUE(src_bl.contents_equal(dst_bl)); + offset += read_size; + } + } + + void open_image(librados::IoCtx& io_ctx, const std::string &name, + const std::string &id, bool read_only, int flags, + librbd::ImageCtx **ictx) { + *ictx = new librbd::ImageCtx(name, id, nullptr, io_ctx, read_only); + m_ictxs.insert(*ictx); + + ASSERT_EQ(0, (*ictx)->state->open(flags)); + (*ictx)->discard_granularity_bytes = 0; + } + + void open_image(librados::IoCtx& io_ctx, const std::string &name, + librbd::ImageCtx **ictx) { + open_image(io_ctx, name, "", false, 0, ictx); + } + + void migration_prepare(librados::IoCtx& dst_io_ctx, + const std::string &dst_name, int r = 0) { + std::cout << __func__ << std::endl; + + close_image(m_ictx); + m_ictx = nullptr; + + EXPECT_EQ(r, librbd::api::Migration<>::prepare(m_ioctx, m_image_name, + dst_io_ctx, dst_name, + m_opts)); + if (r == 0) { + open_image(dst_io_ctx, dst_name, &m_ictx); + } else { + open_image(m_ioctx, m_image_name, &m_ictx); + } + compare("after prepare"); + } + + void migration_execute(librados::IoCtx& io_ctx, const std::string &name, + int r = 0) { + std::cout << __func__ << std::endl; + + librbd::NoOpProgressContext no_op; + EXPECT_EQ(r, librbd::api::Migration<>::execute(io_ctx, name, no_op)); + } + + void migration_abort(librados::IoCtx& io_ctx, const std::string &name, + int r = 0) { + std::cout << __func__ << std::endl; + + std::string dst_name = m_ictx->name; + close_image(m_ictx); + m_ictx = nullptr; + + librbd::NoOpProgressContext no_op; + EXPECT_EQ(r, librbd::api::Migration<>::abort(io_ctx, name, no_op)); + + if (r == 0) { + open_image(m_ioctx, m_image_name, &m_ictx); + } else { + open_image(m_ioctx, dst_name, &m_ictx); + } + + compare("after abort"); + } + + void migration_commit(librados::IoCtx& io_ctx, const std::string &name) { + std::cout << __func__ << std::endl; + + librbd::NoOpProgressContext no_op; + EXPECT_EQ(0, librbd::api::Migration<>::commit(io_ctx, name, no_op)); + + compare("after commit"); + } + + void migration_status(librbd::image_migration_state_t state) { + librbd::image_migration_status_t status; + EXPECT_EQ(0, librbd::api::Migration<>::status(m_ioctx, m_image_name, + &status)); + EXPECT_EQ(status.source_pool_id, m_ioctx.get_id()); + EXPECT_EQ(status.source_pool_namespace, m_ioctx.get_namespace()); + EXPECT_EQ(status.source_image_name, m_image_name); + EXPECT_EQ(status.source_image_id, m_image_id); + EXPECT_EQ(status.dest_pool_id, m_ictx->md_ctx.get_id()); + EXPECT_EQ(status.dest_pool_namespace, m_ictx->md_ctx.get_namespace()); + EXPECT_EQ(status.dest_image_name, m_ictx->name); + EXPECT_EQ(status.dest_image_id, m_ictx->id); + EXPECT_EQ(status.state, state); + } + + void migrate(librados::IoCtx& dst_io_ctx, const std::string &dst_name) { + migration_prepare(dst_io_ctx, dst_name); + migration_status(RBD_IMAGE_MIGRATION_STATE_PREPARED); + migration_execute(dst_io_ctx, dst_name); + migration_status(RBD_IMAGE_MIGRATION_STATE_EXECUTED); + migration_commit(dst_io_ctx, dst_name); + } + + void write(uint64_t off, uint64_t len, char c) { + std::cout << "write: " << c << " " << off << "~" << len << std::endl; + + bufferlist ref_bl; + ref_bl.append(std::string(len, c)); + ASSERT_EQ(static_cast<ssize_t>(len), + api::Io<>::write(*m_ref_ictx, off, len, std::move(ref_bl), 0)); + bufferlist bl; + bl.append(std::string(len, c)); + ASSERT_EQ(static_cast<ssize_t>(len), + api::Io<>::write(*m_ictx, off, len, std::move(bl), 0)); + } + + void discard(uint64_t off, uint64_t len) { + std::cout << "discard: " << off << "~" << len << std::endl; + + ASSERT_EQ(static_cast<ssize_t>(len), + api::Io<>::discard(*m_ref_ictx, off, len, false)); + ASSERT_EQ(static_cast<ssize_t>(len), + api::Io<>::discard(*m_ictx, off, len, false)); + } + + void flush() { + ASSERT_EQ(0, TestFixture::flush_writeback_cache(m_ref_ictx)); + ASSERT_EQ(0, TestFixture::flush_writeback_cache(m_ictx)); + } + + void snap_create(const std::string &snap_name) { + std::cout << "snap_create: " << snap_name << std::endl; + + flush(); + + ASSERT_EQ(0, TestFixture::snap_create(*m_ref_ictx, snap_name)); + ASSERT_EQ(0, TestFixture::snap_create(*m_ictx, snap_name)); + } + + void snap_protect(const std::string &snap_name) { + std::cout << "snap_protect: " << snap_name << std::endl; + + ASSERT_EQ(0, TestFixture::snap_protect(*m_ref_ictx, snap_name)); + ASSERT_EQ(0, TestFixture::snap_protect(*m_ictx, snap_name)); + } + + void clone(const std::string &snap_name) { + snap_protect(snap_name); + + int order = m_ref_ictx->order; + uint64_t features; + ASSERT_EQ(0, librbd::get_features(m_ref_ictx, &features)); + + std::string ref_clone_name = get_temp_image_name(); + std::string clone_name = get_temp_image_name(); + + std::cout << "clone " << m_ictx->name << " -> " << clone_name + << std::endl; + + ASSERT_EQ(0, librbd::clone(m_ref_ictx->md_ctx, m_ref_ictx->name.c_str(), + snap_name.c_str(), m_ref_ioctx, + ref_clone_name.c_str(), features, &order, + m_ref_ictx->stripe_unit, + m_ref_ictx->stripe_count)); + + ASSERT_EQ(0, librbd::clone(m_ictx->md_ctx, m_ictx->name.c_str(), + snap_name.c_str(), m_ioctx, + clone_name.c_str(), features, &order, + m_ictx->stripe_unit, + m_ictx->stripe_count)); + + close_image(m_ref_ictx); + open_image(m_ref_ioctx, ref_clone_name, &m_ref_ictx); + + close_image(m_ictx); + open_image(m_ioctx, clone_name, &m_ictx); + m_image_name = m_ictx->name; + m_image_id = m_ictx->id; + } + + void resize(uint64_t size) { + std::cout << "resize: " << size << std::endl; + + librbd::NoOpProgressContext no_op; + ASSERT_EQ(0, m_ref_ictx->operations->resize(size, true, no_op)); + ASSERT_EQ(0, m_ictx->operations->resize(size, true, no_op)); + } + + void test_no_snaps() { + uint64_t len = (1 << m_ictx->order) * 2 + 1; + write(0 * len, len, '1'); + write(2 * len, len, '1'); + flush(); + } + + void test_snaps() { + uint64_t len = (1 << m_ictx->order) * 2 + 1; + write(0 * len, len, '1'); + snap_create("snap1"); + write(1 * len, len, '1'); + + write(0 * len, 1000, 'X'); + discard(1000 + 10, 1000); + + snap_create("snap2"); + + write(1 * len, 1000, 'X'); + discard(2 * len + 10, 1000); + + uint64_t size = m_ictx->size; + + resize(size << 1); + + write(size - 1, len, '2'); + + snap_create("snap3"); + + resize(size); + + discard(size - 1, 1); + + flush(); + } + + void test_clone() { + uint64_t len = (1 << m_ictx->order) * 2 + 1; + write(0 * len, len, 'X'); + write(2 * len, len, 'X'); + + snap_create("snap"); + clone("snap"); + + write(0, 1000, 'X'); + discard(1010, 1000); + + snap_create("snap"); + clone("snap"); + + write(1000, 1000, 'X'); + discard(2010, 1000); + + flush(); + } + + template <typename L> + void test_migrate_parent(uint32_t clone_format, L&& test) { + REQUIRE_FEATURE(RBD_FEATURE_LAYERING); + + std::string prev_clone_format; + ASSERT_EQ(0, _rados.conf_get("rbd_default_clone_format", + prev_clone_format)); + ASSERT_EQ(0, _rados.conf_set("rbd_default_clone_format", + stringify(clone_format).c_str())); + BOOST_SCOPE_EXIT_TPL(&prev_clone_format) { + _rados.conf_set("rbd_default_clone_format", prev_clone_format.c_str()); + } BOOST_SCOPE_EXIT_END; + + write(0, 10, 'A'); + snap_create("snap1"); + snap_protect("snap1"); + + int order = m_ictx->order; + uint64_t features; + ASSERT_EQ(0, librbd::get_features(m_ictx, &features)); + + std::string clone_name = get_temp_image_name(); + ASSERT_EQ(0, librbd::clone(m_ictx->md_ctx, m_ictx->name.c_str(), "snap1", + m_ioctx, clone_name.c_str(), features, &order, + m_ictx->stripe_unit, m_ictx->stripe_count)); + + librbd::ImageCtx *child_ictx; + open_image(m_ioctx, clone_name, &child_ictx); + + test(child_ictx); + + ASSERT_EQ(0, child_ictx->state->refresh()); + + bufferlist bl; + bufferptr ptr(10); + bl.push_back(ptr); + librbd::io::ReadResult result{&bl}; + ASSERT_EQ(10, api::Io<>::read( + *child_ictx, 0, 10, librbd::io::ReadResult{result}, 0)); + bufferlist ref_bl; + ref_bl.append(std::string(10, 'A')); + ASSERT_TRUE(ref_bl.contents_equal(bl)); + close_image(child_ictx); + } + + void test_stress(const std::string &snap_name_prefix = "snap", + char start_char = 'A') { + uint64_t initial_size = m_ictx->size; + + int nsnaps = 4; + const char *c = getenv("TEST_RBD_MIGRATION_STRESS_NSNAPS"); + if (c != NULL) { + std::stringstream ss(c); + ASSERT_TRUE(ss >> nsnaps); + } + + int nwrites = 4; + c = getenv("TEST_RBD_MIGRATION_STRESS_NWRITES"); + if (c != NULL) { + std::stringstream ss(c); + ASSERT_TRUE(ss >> nwrites); + } + + for (int i = 0; i < nsnaps; i++) { + for (int j = 0; j < nwrites; j++) { + size_t len = rand() % ((1 << m_ictx->order) * 2); + ASSERT_GT(m_ictx->size, len); + uint64_t off = std::min(static_cast<uint64_t>(rand() % m_ictx->size), + static_cast<uint64_t>(m_ictx->size - len)); + write(off, len, start_char + i); + + len = rand() % ((1 << m_ictx->order) * 2); + ASSERT_GT(m_ictx->size, len); + off = std::min(static_cast<uint64_t>(rand() % m_ictx->size), + static_cast<uint64_t>(m_ictx->size - len)); + discard(off, len); + } + + std::string snap_name = snap_name_prefix + stringify(i); + snap_create(snap_name); + + if (m_ictx->test_features(RBD_FEATURE_LAYERING) && + !m_ictx->test_features(RBD_FEATURE_MIGRATING) && + rand() % 4) { + clone(snap_name); + } + + if (rand() % 2) { + librbd::NoOpProgressContext no_op; + uint64_t new_size = initial_size + rand() % m_ictx->size; + resize(new_size); + ASSERT_EQ(new_size, m_ictx->size); + } + } + flush(); + } + + void test_stress2(bool concurrent) { + test_stress(); + + migration_prepare(m_ioctx, m_image_name); + migration_status(RBD_IMAGE_MIGRATION_STATE_PREPARED); + + std::thread user([this]() { + test_stress("user", 'a'); + for (int i = 0; i < 5; i++) { + uint64_t off = (i + 1) * m_ictx->size / 10; + uint64_t len = m_ictx->size / 40; + write(off, len, '1' + i); + + off += len / 4; + len /= 2; + discard(off, len); + } + flush(); + }); + + if (concurrent) { + librados::IoCtx io_ctx; + EXPECT_EQ(0, _rados.ioctx_create2(m_ioctx.get_id(), io_ctx)); + migration_execute(io_ctx, m_image_name); + io_ctx.close(); + user.join(); + } else { + user.join(); + compare("before execute"); + migration_execute(m_ioctx, m_image_name); + } + + migration_status(RBD_IMAGE_MIGRATION_STATE_EXECUTED); + migration_commit(m_ioctx, m_image_name); + } + + static std::string _other_pool_name; + static librados::IoCtx _other_pool_ioctx; + + std::string m_image_id; + librbd::ImageCtx *m_ictx = nullptr; + librados::IoCtx m_ref_ioctx; + librbd::ImageCtx *m_ref_ictx = nullptr; + librbd::ImageOptions m_opts; +}; + +std::string TestMigration::_other_pool_name; +librados::IoCtx TestMigration::_other_pool_ioctx; + +TEST_F(TestMigration, Empty) +{ + uint64_t features = m_ictx->features ^ RBD_FEATURE_LAYERING; + features &= ~RBD_FEATURE_DIRTY_CACHE; + ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_FEATURES, features)); + + migrate(m_ioctx, m_image_name); + + ASSERT_EQ(features, m_ictx->features); +} + +TEST_F(TestMigration, OtherName) +{ + std::string name = get_temp_image_name(); + + migrate(m_ioctx, name); + + ASSERT_EQ(name, m_ictx->name); +} + +TEST_F(TestMigration, OtherPool) +{ + migrate(_other_pool_ioctx, m_image_name); + + ASSERT_EQ(_other_pool_ioctx.get_id(), m_ictx->md_ctx.get_id()); +} + +TEST_F(TestMigration, OtherNamespace) +{ + ASSERT_EQ(0, librbd::api::Namespace<>::create(_other_pool_ioctx, "ns1")); + _other_pool_ioctx.set_namespace("ns1"); + + migrate(_other_pool_ioctx, m_image_name); + + ASSERT_EQ(_other_pool_ioctx.get_id(), m_ictx->md_ctx.get_id()); + ASSERT_EQ(_other_pool_ioctx.get_namespace(), m_ictx->md_ctx.get_namespace()); + _other_pool_ioctx.set_namespace(""); +} + +TEST_F(TestMigration, DataPool) +{ + ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_DATA_POOL, + _other_pool_ioctx.get_pool_name().c_str())); + + migrate(m_ioctx, m_image_name); + + ASSERT_EQ(_other_pool_ioctx.get_id(), m_ictx->data_ctx.get_id()); +} + +TEST_F(TestMigration, AbortAfterPrepare) +{ + migration_prepare(m_ioctx, m_image_name); + migration_status(RBD_IMAGE_MIGRATION_STATE_PREPARED); + migration_abort(m_ioctx, m_image_name); +} + +TEST_F(TestMigration, AbortAfterFailedPrepare) +{ + ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_DATA_POOL, "INVALID_POOL")); + + migration_prepare(m_ioctx, m_image_name, -ENOENT); + + // Migration is automatically aborted if prepare failed +} + +TEST_F(TestMigration, AbortAfterExecute) +{ + migration_prepare(m_ioctx, m_image_name); + migration_status(RBD_IMAGE_MIGRATION_STATE_PREPARED); + migration_execute(m_ioctx, m_image_name); + migration_status(RBD_IMAGE_MIGRATION_STATE_EXECUTED); + migration_abort(m_ioctx, m_image_name); +} + +TEST_F(TestMigration, OtherPoolAbortAfterExecute) +{ + migration_prepare(_other_pool_ioctx, m_image_name); + migration_status(RBD_IMAGE_MIGRATION_STATE_PREPARED); + migration_execute(_other_pool_ioctx, m_image_name); + migration_status(RBD_IMAGE_MIGRATION_STATE_EXECUTED); + migration_abort(_other_pool_ioctx, m_image_name); +} + +TEST_F(TestMigration, OtherNamespaceAbortAfterExecute) +{ + ASSERT_EQ(0, librbd::api::Namespace<>::create(_other_pool_ioctx, "ns2")); + _other_pool_ioctx.set_namespace("ns2"); + + migration_prepare(_other_pool_ioctx, m_image_name); + migration_status(RBD_IMAGE_MIGRATION_STATE_PREPARED); + migration_execute(_other_pool_ioctx, m_image_name); + migration_status(RBD_IMAGE_MIGRATION_STATE_EXECUTED); + migration_abort(_other_pool_ioctx, m_image_name); + + _other_pool_ioctx.set_namespace(""); + ASSERT_EQ(0, librbd::api::Namespace<>::remove(_other_pool_ioctx, "ns2")); +} + +TEST_F(TestMigration, MirroringSamePool) +{ + REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); + + ASSERT_EQ(0, librbd::api::Mirror<>::mode_set(m_ioctx, RBD_MIRROR_MODE_IMAGE)); + + ASSERT_EQ(0, librbd::api::Mirror<>::image_enable( + m_ictx, RBD_MIRROR_IMAGE_MODE_JOURNAL, false)); + librbd::mirror_image_info_t info; + ASSERT_EQ(0, librbd::api::Mirror<>::image_get_info(m_ictx, &info)); + ASSERT_EQ(RBD_MIRROR_IMAGE_ENABLED, info.state); + + migrate(m_ioctx, m_image_name); + + ASSERT_EQ(0, librbd::api::Mirror<>::image_get_info(m_ictx, &info)); + ASSERT_EQ(RBD_MIRROR_IMAGE_ENABLED, info.state); +} + +TEST_F(TestMigration, MirroringAbort) +{ + REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); + + ASSERT_EQ(0, librbd::api::Mirror<>::mode_set(m_ioctx, RBD_MIRROR_MODE_IMAGE)); + + ASSERT_EQ(0, librbd::api::Mirror<>::image_enable( + m_ictx, RBD_MIRROR_IMAGE_MODE_JOURNAL, false)); + librbd::mirror_image_info_t info; + ASSERT_EQ(0, librbd::api::Mirror<>::image_get_info(m_ictx, &info)); + ASSERT_EQ(RBD_MIRROR_IMAGE_ENABLED, info.state); + + migration_prepare(m_ioctx, m_image_name); + migration_status(RBD_IMAGE_MIGRATION_STATE_PREPARED); + ASSERT_EQ(0, librbd::api::Mirror<>::image_get_info(m_ictx, &info)); + ASSERT_EQ(RBD_MIRROR_IMAGE_DISABLED, info.state); + + migration_abort(m_ioctx, m_image_name); + + ASSERT_EQ(0, librbd::api::Mirror<>::image_get_info(m_ictx, &info)); + ASSERT_EQ(RBD_MIRROR_IMAGE_ENABLED, info.state); +} + +TEST_F(TestMigration, MirroringOtherPoolDisabled) +{ + REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); + + ASSERT_EQ(0, librbd::api::Mirror<>::mode_set(m_ioctx, RBD_MIRROR_MODE_IMAGE)); + + ASSERT_EQ(0, librbd::api::Mirror<>::image_enable( + m_ictx, RBD_MIRROR_IMAGE_MODE_JOURNAL, false)); + librbd::mirror_image_info_t info; + ASSERT_EQ(0, librbd::api::Mirror<>::image_get_info(m_ictx, &info)); + ASSERT_EQ(RBD_MIRROR_IMAGE_ENABLED, info.state); + + migrate(_other_pool_ioctx, m_image_name); + + ASSERT_EQ(0, librbd::api::Mirror<>::image_get_info(m_ictx, &info)); + ASSERT_EQ(RBD_MIRROR_IMAGE_DISABLED, info.state); +} + +TEST_F(TestMigration, MirroringOtherPoolEnabled) +{ + REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); + + ASSERT_EQ(0, librbd::api::Mirror<>::mode_set(m_ioctx, RBD_MIRROR_MODE_IMAGE)); + ASSERT_EQ(0, librbd::api::Mirror<>::mode_set(_other_pool_ioctx, + RBD_MIRROR_MODE_IMAGE)); + + ASSERT_EQ(0, librbd::api::Mirror<>::image_enable( + m_ictx, RBD_MIRROR_IMAGE_MODE_JOURNAL, false)); + librbd::mirror_image_info_t info; + ASSERT_EQ(0, librbd::api::Mirror<>::image_get_info(m_ictx, &info)); + ASSERT_EQ(RBD_MIRROR_IMAGE_ENABLED, info.state); + + migrate(_other_pool_ioctx, m_image_name); + + ASSERT_EQ(0, librbd::api::Mirror<>::image_get_info(m_ictx, &info)); + ASSERT_EQ(RBD_MIRROR_IMAGE_ENABLED, info.state); +} + +TEST_F(TestMigration, MirroringPool) +{ + REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); + + ASSERT_EQ(0, librbd::api::Mirror<>::mode_set(_other_pool_ioctx, + RBD_MIRROR_MODE_POOL)); + librbd::mirror_image_info_t info; + ASSERT_EQ(0, librbd::api::Mirror<>::image_get_info(m_ictx, &info)); + ASSERT_EQ(RBD_MIRROR_IMAGE_DISABLED, info.state); + + migrate(_other_pool_ioctx, m_image_name); + + ASSERT_EQ(0, librbd::api::Mirror<>::image_get_info(m_ictx, &info)); + ASSERT_EQ(RBD_MIRROR_IMAGE_ENABLED, info.state); +} + +TEST_F(TestMigration, Group) +{ + REQUIRE_FORMAT_V2(); + + ASSERT_EQ(0, librbd::api::Group<>::create(m_ioctx, "123")); + ASSERT_EQ(0, librbd::api::Group<>::image_add(m_ioctx, "123", m_ioctx, + m_image_name.c_str())); + librbd::group_info_t info; + ASSERT_EQ(0, librbd::api::Group<>::image_get_group(m_ictx, &info)); + + std::string name = get_temp_image_name(); + + migrate(m_ioctx, name); + + ASSERT_EQ(0, librbd::api::Group<>::image_get_group(m_ictx, &info)); + ASSERT_EQ(info.name, "123"); + + ASSERT_EQ(0, librbd::api::Group<>::image_remove(m_ioctx, "123", m_ioctx, + name.c_str())); + ASSERT_EQ(0, librbd::api::Group<>::remove(m_ioctx, "123")); +} + +TEST_F(TestMigration, GroupAbort) +{ + REQUIRE_FORMAT_V2(); + + ASSERT_EQ(0, librbd::api::Group<>::create(m_ioctx, "123")); + ASSERT_EQ(0, librbd::api::Group<>::image_add(m_ioctx, "123", m_ioctx, + m_image_name.c_str())); + librbd::group_info_t info; + ASSERT_EQ(0, librbd::api::Group<>::image_get_group(m_ictx, &info)); + + std::string name = get_temp_image_name(); + + migration_prepare(m_ioctx, name); + migration_status(RBD_IMAGE_MIGRATION_STATE_PREPARED); + + ASSERT_EQ(0, librbd::api::Group<>::image_get_group(m_ictx, &info)); + ASSERT_EQ(info.name, "123"); + + migration_abort(m_ioctx, m_image_name); + + ASSERT_EQ(0, librbd::api::Group<>::image_get_group(m_ictx, &info)); + ASSERT_EQ(info.name, "123"); + + ASSERT_EQ(0, librbd::api::Group<>::image_remove(m_ioctx, "123", m_ioctx, + m_image_name.c_str())); + ASSERT_EQ(0, librbd::api::Group<>::remove(m_ioctx, "123")); +} + +TEST_F(TestMigration, NoSnaps) +{ + test_no_snaps(); + migrate(m_ioctx, m_image_name); +} + +TEST_F(TestMigration, NoSnapsOtherPool) +{ + test_no_snaps(); + + test_no_snaps(); + migrate(_other_pool_ioctx, m_image_name); +} + +TEST_F(TestMigration, NoSnapsDataPool) +{ + test_no_snaps(); + + ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_DATA_POOL, + _other_pool_ioctx.get_pool_name().c_str())); + migrate(m_ioctx, m_image_name); + + EXPECT_EQ(_other_pool_ioctx.get_id(), m_ictx->data_ctx.get_id()); +} + +TEST_F(TestMigration, NoSnapsShrinkAfterPrepare) +{ + test_no_snaps(); + + migration_prepare(m_ioctx, m_image_name); + migration_status(RBD_IMAGE_MIGRATION_STATE_PREPARED); + + resize(m_ictx->size >> 1); + + migration_execute(m_ioctx, m_image_name); + migration_status(RBD_IMAGE_MIGRATION_STATE_EXECUTED); + migration_commit(m_ioctx, m_image_name); +} + +TEST_F(TestMigration, NoSnapsShrinkToZeroBeforePrepare) +{ + test_no_snaps(); + resize(0); + + migrate(m_ioctx, m_image_name); +} + +TEST_F(TestMigration, NoSnapsShrinkToZeroAfterPrepare) +{ + test_no_snaps(); + + migration_prepare(m_ioctx, m_image_name); + migration_status(RBD_IMAGE_MIGRATION_STATE_PREPARED); + + resize(0); + + migration_execute(m_ioctx, m_image_name); + migration_status(RBD_IMAGE_MIGRATION_STATE_EXECUTED); + migration_commit(m_ioctx, m_image_name); +} + +TEST_F(TestMigration, NoSnapsExpandAfterPrepare) +{ + test_no_snaps(); + + migration_prepare(m_ioctx, m_image_name); + migration_status(RBD_IMAGE_MIGRATION_STATE_PREPARED); + + resize(m_ictx->size << 1); + + migration_execute(m_ioctx, m_image_name); + migration_status(RBD_IMAGE_MIGRATION_STATE_EXECUTED); + migration_commit(m_ioctx, m_image_name); +} + +TEST_F(TestMigration, NoSnapsSnapAfterPrepare) +{ + test_no_snaps(); + + migration_prepare(m_ioctx, m_image_name); + migration_status(RBD_IMAGE_MIGRATION_STATE_PREPARED); + + snap_create("after_prepare_snap"); + resize(m_ictx->size >> 1); + write(0, 1000, '*'); + + migration_execute(m_ioctx, m_image_name); + migration_status(RBD_IMAGE_MIGRATION_STATE_EXECUTED); + migration_commit(m_ioctx, m_image_name); +} + +TEST_F(TestMigration, Snaps) +{ + test_snaps(); + migrate(m_ioctx, m_image_name); +} + +TEST_F(TestMigration, SnapsOtherPool) +{ + test_snaps(); + + test_no_snaps(); + migrate(_other_pool_ioctx, m_image_name); + + EXPECT_EQ(_other_pool_ioctx.get_id(), m_ictx->md_ctx.get_id()); +} + +TEST_F(TestMigration, SnapsDataPool) +{ + test_snaps(); + + ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_DATA_POOL, + _other_pool_ioctx.get_pool_name().c_str())); + migrate(m_ioctx, m_image_name); + + EXPECT_EQ(_other_pool_ioctx.get_id(), m_ictx->data_ctx.get_id()); +} + +TEST_F(TestMigration, SnapsShrinkAfterPrepare) +{ + test_snaps(); + + migration_prepare(m_ioctx, m_image_name); + migration_status(RBD_IMAGE_MIGRATION_STATE_PREPARED); + + resize(m_ictx->size >> 1); + + migration_execute(m_ioctx, m_image_name); + migration_status(RBD_IMAGE_MIGRATION_STATE_EXECUTED); + migration_commit(m_ioctx, m_image_name); +} + +TEST_F(TestMigration, SnapsShrinkToZeroBeforePrepare) +{ + test_snaps(); + resize(0); + + migrate(m_ioctx, m_image_name); +} + +TEST_F(TestMigration, SnapsShrinkToZeroAfterPrepare) +{ + test_snaps(); + + migration_prepare(m_ioctx, m_image_name); + migration_status(RBD_IMAGE_MIGRATION_STATE_PREPARED); + + resize(0); + + migration_execute(m_ioctx, m_image_name); + migration_status(RBD_IMAGE_MIGRATION_STATE_EXECUTED); + migration_commit(m_ioctx, m_image_name); +} + +TEST_F(TestMigration, SnapsExpandAfterPrepare) +{ + test_snaps(); + + migration_prepare(m_ioctx, m_image_name); + migration_status(RBD_IMAGE_MIGRATION_STATE_PREPARED); + + auto size = m_ictx->size; + resize(size << 1); + write(size, 1000, '*'); + + migration_execute(m_ioctx, m_image_name); + migration_status(RBD_IMAGE_MIGRATION_STATE_EXECUTED); + migration_commit(m_ioctx, m_image_name); +} + +TEST_F(TestMigration, SnapsExpandAfterPrepare2) +{ + auto size = m_ictx->size; + + write(size >> 1, 10, 'X'); + snap_create("snap1"); + resize(size >> 1); + + migration_prepare(m_ioctx, m_image_name); + migration_status(RBD_IMAGE_MIGRATION_STATE_PREPARED); + + resize(size); + write(size >> 1, 5, 'Y'); + + compare("before execute"); + + migration_execute(m_ioctx, m_image_name); + migration_status(RBD_IMAGE_MIGRATION_STATE_EXECUTED); + migration_commit(m_ioctx, m_image_name); +} + +TEST_F(TestMigration, SnapsSnapAfterPrepare) +{ + test_snaps(); + + migration_prepare(m_ioctx, m_image_name); + migration_status(RBD_IMAGE_MIGRATION_STATE_PREPARED); + + auto ictx = new librbd::ImageCtx(m_ictx->name.c_str(), "", "snap3", m_ioctx, + false); + ASSERT_EQ(0, ictx->state->open(0)); + EXPECT_EQ(0, librbd::api::Image<>::snap_set( + m_ref_ictx, cls::rbd::UserSnapshotNamespace(), "snap3")); + compare_snaps("opened after prepare snap3", m_ref_ictx, ictx); + EXPECT_EQ(0, librbd::api::Image<>::snap_set( + m_ref_ictx, cls::rbd::UserSnapshotNamespace(), nullptr)); + EXPECT_EQ(0, ictx->state->close()); + + snap_create("after_prepare_snap"); + resize(m_ictx->size >> 1); + write(0, 1000, '*'); + + migration_execute(m_ioctx, m_image_name); + migration_status(RBD_IMAGE_MIGRATION_STATE_EXECUTED); + migration_commit(m_ioctx, m_image_name); +} + +TEST_F(TestMigration, SnapsSnapExpandAfterPrepare) +{ + test_snaps(); + + migration_prepare(m_ioctx, m_image_name); + migration_status(RBD_IMAGE_MIGRATION_STATE_PREPARED); + + snap_create("after_prepare_snap"); + auto size = m_ictx->size; + resize(size << 1); + write(size, 1000, '*'); + + migration_execute(m_ioctx, m_image_name); + migration_status(RBD_IMAGE_MIGRATION_STATE_EXECUTED); + migration_commit(m_ioctx, m_image_name); +} + +TEST_F(TestMigration, Clone) +{ + REQUIRE_FEATURE(RBD_FEATURE_LAYERING); + + test_clone(); + migrate(m_ioctx, m_image_name); +} + +TEST_F(TestMigration, CloneParent) { + REQUIRE_FEATURE(RBD_FEATURE_LAYERING); + + snap_create("snap"); + + librbd::linked_image_spec_t expected_parent_image; + expected_parent_image.image_id = m_ictx->id; + expected_parent_image.image_name = m_ictx->name; + + auto it = m_ictx->snap_ids.find({cls::rbd::UserSnapshotNamespace{}, "snap"}); + ASSERT_TRUE(it != m_ictx->snap_ids.end()); + + librbd::snap_spec_t expected_parent_snap; + expected_parent_snap.id = it->second; + + clone("snap"); + migration_prepare(m_ioctx, m_image_name); + + librbd::linked_image_spec_t parent_image; + librbd::snap_spec_t parent_snap; + ASSERT_EQ(0, librbd::api::Image<>::get_parent(m_ictx, &parent_image, + &parent_snap)); + ASSERT_EQ(expected_parent_image.image_id, parent_image.image_id); + ASSERT_EQ(expected_parent_image.image_name, parent_image.image_name); + ASSERT_EQ(expected_parent_snap.id, parent_snap.id); + + migration_abort(m_ioctx, m_image_name); +} + + +TEST_F(TestMigration, CloneUpdateAfterPrepare) +{ + REQUIRE_FEATURE(RBD_FEATURE_LAYERING); + + write(0, 10, 'X'); + snap_create("snap"); + clone("snap"); + + migration_prepare(m_ioctx, m_image_name); + + write(0, 1, 'Y'); + + migration_execute(m_ioctx, m_image_name); + migration_commit(m_ioctx, m_image_name); +} + +TEST_F(TestMigration, TriggerAssertSnapcSeq) +{ + auto size = m_ictx->size; + + write((size >> 1) + 0, 10, 'A'); + snap_create("snap1"); + write((size >> 1) + 1, 10, 'B'); + + migration_prepare(m_ioctx, m_image_name); + + // copyup => deep copy (first time) + write((size >> 1) + 2, 10, 'C'); + + // preserve data before resizing + snap_create("snap2"); + + // decrease head overlap + resize(size >> 1); + + // migrate object => deep copy (second time) => assert_snapc_seq => -ERANGE + migration_execute(m_ioctx, m_image_name); + migration_commit(m_ioctx, m_image_name); +} + +TEST_F(TestMigration, SnapTrimBeforePrepare) +{ + auto size = m_ictx->size; + + write(size >> 1, 10, 'A'); + snap_create("snap1"); + resize(size >> 1); + + migration_prepare(m_ioctx, m_image_name); + + resize(size); + snap_create("snap3"); + write(size >> 1, 10, 'B'); + snap_create("snap4"); + resize(size >> 1); + + migration_execute(m_ioctx, m_image_name); + migration_commit(m_ioctx, m_image_name); +} + +TEST_F(TestMigration, AbortInUseImage) { + migration_prepare(m_ioctx, m_image_name); + migration_status(RBD_IMAGE_MIGRATION_STATE_PREPARED); + + librbd::NoOpProgressContext no_op; + EXPECT_EQ(-EBUSY, librbd::api::Migration<>::abort(m_ioctx, m_ictx->name, + no_op)); +} + +TEST_F(TestMigration, AbortWithoutSnapshots) { + test_no_snaps(); + migration_prepare(m_ioctx, m_image_name); + migration_status(RBD_IMAGE_MIGRATION_STATE_PREPARED); + test_no_snaps(); + migration_abort(m_ioctx, m_image_name); +} + +TEST_F(TestMigration, AbortWithSnapshots) { + test_snaps(); + migration_prepare(m_ioctx, m_image_name); + migration_status(RBD_IMAGE_MIGRATION_STATE_PREPARED); + + test_no_snaps(); + flush(); + ASSERT_EQ(0, TestFixture::snap_create(*m_ictx, "dst-only-snap")); + + test_no_snaps(); + + migration_abort(m_ioctx, m_image_name); +} + +TEST_F(TestMigration, CloneV1Parent) +{ + const uint32_t CLONE_FORMAT = 1; + test_migrate_parent( + CLONE_FORMAT, [this](librbd::ImageCtx *) { + migrate(m_ioctx, m_image_name); + }); +} + +TEST_F(TestMigration, CloneV2Parent) +{ + const uint32_t CLONE_FORMAT = 2; + test_migrate_parent( + CLONE_FORMAT, [this](librbd::ImageCtx *) { + migrate(m_ioctx, m_image_name); + }); +} + +TEST_F(TestMigration, CloneV1ParentAbort) +{ + const uint32_t CLONE_FORMAT = 1; + test_migrate_parent( + CLONE_FORMAT, [this](librbd::ImageCtx *) { + migration_prepare(m_ioctx, m_image_name); + migration_abort(m_ioctx, m_image_name); + }); +} + +TEST_F(TestMigration, CloneV2ParentAbort) +{ + const uint32_t CLONE_FORMAT = 2; + test_migrate_parent( + CLONE_FORMAT, [this](librbd::ImageCtx *) { + migration_prepare(m_ioctx, m_image_name); + migration_abort(m_ioctx, m_image_name); + }); +} + +TEST_F(TestMigration, CloneV1ParentAbortFixIncompleteChildReattach) +{ + const uint32_t CLONE_FORMAT = 1; + test_migrate_parent( + CLONE_FORMAT, [this](librbd::ImageCtx *child_ictx) { + auto src_image_id = m_ictx->id; + migration_prepare(m_ioctx, m_image_name); + // Attach the child to both source and destination + // to emulate a crash when re-attaching the child + librbd::ImageCtx *src_ictx; + open_image(m_ioctx, "", src_image_id, false, + librbd::OPEN_FLAG_IGNORE_MIGRATING, &src_ictx); + C_SaferCond cond; + auto req = librbd::image::AttachChildRequest<>::create( + child_ictx, src_ictx, src_ictx->snaps[0], nullptr, 0, + CLONE_FORMAT, &cond); + req->send(); + ASSERT_EQ(0, cond.wait()); + close_image(src_ictx); + migration_abort(m_ioctx, m_image_name); + }); +} + +TEST_F(TestMigration, CloneV1ParentAbortFixParentReattach) +{ + const uint32_t CLONE_FORMAT = 1; + test_migrate_parent( + CLONE_FORMAT, [this](librbd::ImageCtx *child_ictx) { + auto src_image_id = m_ictx->id; + migration_prepare(m_ioctx, m_image_name); + // Re-attach the child back to the source to emulate a crash + // after the parent reattach but before the child reattach + librbd::ImageCtx *src_ictx; + open_image(m_ioctx, "", src_image_id, false, + librbd::OPEN_FLAG_IGNORE_MIGRATING, &src_ictx); + C_SaferCond cond; + auto req = librbd::image::AttachChildRequest<>::create( + child_ictx, src_ictx, src_ictx->snaps[0], m_ictx, + m_ictx->snaps[0], CLONE_FORMAT, &cond); + req->send(); + ASSERT_EQ(0, cond.wait()); + close_image(src_ictx); + migration_abort(m_ioctx, m_image_name); + }); +} + +TEST_F(TestMigration, CloneV1ParentAbortRelinkNotNeeded) +{ + const uint32_t CLONE_FORMAT = 1; + test_migrate_parent( + CLONE_FORMAT, [this](librbd::ImageCtx *child_ictx) { + auto src_image_id = m_ictx->id; + auto parent_spec = child_ictx->parent_md.spec; + parent_spec.image_id = m_ictx->id; + parent_spec.snap_id = m_ictx->snaps[0]; + auto parent_overlap = child_ictx->parent_md.overlap; + migration_prepare(m_ioctx, m_image_name); + // Relink the child back to emulate a crash + // before relinking the child + C_SaferCond cond; + auto req = librbd::image::AttachParentRequest<>::create( + *child_ictx, parent_spec, parent_overlap, true, &cond); + req->send(); + ASSERT_EQ(0, cond.wait()); + librbd::ImageCtx *src_ictx; + open_image(m_ioctx, "", src_image_id, false, + librbd::OPEN_FLAG_IGNORE_MIGRATING, &src_ictx); + C_SaferCond cond1; + auto req1 = librbd::image::AttachChildRequest<>::create( + child_ictx, src_ictx, src_ictx->snaps[0], m_ictx, + m_ictx->snaps[0], CLONE_FORMAT, &cond1); + req1->send(); + ASSERT_EQ(0, cond1.wait()); + close_image(src_ictx); + migration_abort(m_ioctx, m_image_name); + }); +} + +TEST_F(TestMigration, CloneV2ParentAbortFixIncompleteChildReattach) +{ + const uint32_t CLONE_FORMAT = 2; + test_migrate_parent( + CLONE_FORMAT, [this](librbd::ImageCtx *child_ictx) { + auto src_image_id = m_ictx->id; + migration_prepare(m_ioctx, m_image_name); + // Attach the child to both source and destination + // to emulate a crash when re-attaching the child + librbd::ImageCtx *src_ictx; + open_image(m_ioctx, "", src_image_id, false, + librbd::OPEN_FLAG_IGNORE_MIGRATING, &src_ictx); + C_SaferCond cond; + auto req = librbd::image::AttachChildRequest<>::create( + child_ictx, src_ictx, src_ictx->snaps[0], nullptr, 0, + CLONE_FORMAT, &cond); + req->send(); + ASSERT_EQ(0, cond.wait()); + close_image(src_ictx); + migration_abort(m_ioctx, m_image_name); + }); +} + +TEST_F(TestMigration, CloneV2ParentAbortFixParentReattach) +{ + const uint32_t CLONE_FORMAT = 2; + test_migrate_parent( + CLONE_FORMAT, [this](librbd::ImageCtx *child_ictx) { + auto src_image_id = m_ictx->id; + migration_prepare(m_ioctx, m_image_name); + // Re-attach the child back to the source to emulate a crash + // after the parent reattach but before the child reattach + librbd::ImageCtx *src_ictx; + open_image(m_ioctx, "", src_image_id, false, + librbd::OPEN_FLAG_IGNORE_MIGRATING, &src_ictx); + C_SaferCond cond; + auto req = librbd::image::AttachChildRequest<>::create( + child_ictx, src_ictx, src_ictx->snaps[0], m_ictx, + m_ictx->snaps[0], CLONE_FORMAT, &cond); + req->send(); + ASSERT_EQ(0, cond.wait()); + close_image(src_ictx); + migration_abort(m_ioctx, m_image_name); + }); +} + +TEST_F(TestMigration, CloneV2ParentAbortRelinkNotNeeded) +{ + const uint32_t CLONE_FORMAT = 2; + test_migrate_parent( + CLONE_FORMAT, [this](librbd::ImageCtx *child_ictx) { + auto src_image_id = m_ictx->id; + auto parent_spec = child_ictx->parent_md.spec; + parent_spec.image_id = m_ictx->id; + parent_spec.snap_id = m_ictx->snaps[0]; + auto parent_overlap = child_ictx->parent_md.overlap; + migration_prepare(m_ioctx, m_image_name); + // Relink the child back to emulate a crash + // before relinking the child + C_SaferCond cond; + auto req = librbd::image::AttachParentRequest<>::create( + *child_ictx, parent_spec, parent_overlap, true, &cond); + req->send(); + ASSERT_EQ(0, cond.wait()); + librbd::ImageCtx *src_ictx; + open_image(m_ioctx, "", src_image_id, false, + librbd::OPEN_FLAG_IGNORE_MIGRATING, &src_ictx); + C_SaferCond cond1; + auto req1 = librbd::image::AttachChildRequest<>::create( + child_ictx, src_ictx, src_ictx->snaps[0], m_ictx, + m_ictx->snaps[0], CLONE_FORMAT, &cond1); + req1->send(); + ASSERT_EQ(0, cond1.wait()); + close_image(src_ictx); + migration_abort(m_ioctx, m_image_name); + }); +} + +TEST_F(TestMigration, StressNoMigrate) +{ + test_stress(); + + compare(); +} + +TEST_F(TestMigration, Stress) +{ + test_stress(); + + migrate(m_ioctx, m_image_name); +} + +TEST_F(TestMigration, Stress2) +{ + test_stress2(false); +} + +TEST_F(TestMigration, StressLive) +{ + test_stress2(true); +} + +} // namespace librbd |