tests/qtest/migration: Add a cancel test
The qmp_migrate_cancel() command is poorly tested and code inspection reveals that there might be concurrency issues with its usage. Add a test that runs a migration and calls qmp_migrate_cancel() at specific moments. In order to make the test more deterministic, instead of calling qmp_migrate_cancel() at random moments during migration, do it after the migration status change events are seen. The expected result is that qmp_migrate_cancel() on the source ends migration on the source with the "cancelled" state and ends migration on the destination with the "failed" state. The only exception is that a failed migration should continue in the failed state. Cancelling is not allowed during postcopy (no test is added for this because it's a trivial check in the code). Reviewed-by: Peter Xu <peterx@redhat.com> Message-ID: <20250213175927.19642-9-farosas@suse.de> Signed-off-by: Fabiano Rosas <farosas@suse.de>
This commit is contained in:
parent
aabb2a5b5d
commit
538e03d280
@ -20,6 +20,7 @@
|
|||||||
#include "migration/migration-util.h"
|
#include "migration/migration-util.h"
|
||||||
#include "ppc-util.h"
|
#include "ppc-util.h"
|
||||||
#include "qobject/qlist.h"
|
#include "qobject/qlist.h"
|
||||||
|
#include "qapi-types-migration.h"
|
||||||
#include "qemu/module.h"
|
#include "qemu/module.h"
|
||||||
#include "qemu/option.h"
|
#include "qemu/option.h"
|
||||||
#include "qemu/range.h"
|
#include "qemu/range.h"
|
||||||
@ -536,6 +537,161 @@ static void test_multifd_tcp_cancel(void)
|
|||||||
migrate_end(from, to2, true);
|
migrate_end(from, to2, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_cancel_src_after_failed(QTestState *from, QTestState *to,
|
||||||
|
const char *uri, const char *phase)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* No migrate_incoming_qmp() at the start to force source into
|
||||||
|
* failed state during migrate_qmp().
|
||||||
|
*/
|
||||||
|
|
||||||
|
wait_for_serial("src_serial");
|
||||||
|
migrate_ensure_converge(from);
|
||||||
|
|
||||||
|
migrate_qmp(from, to, uri, NULL, "{}");
|
||||||
|
|
||||||
|
migration_event_wait(from, phase);
|
||||||
|
migrate_cancel(from);
|
||||||
|
|
||||||
|
/* cancelling will not move the migration out of 'failed' */
|
||||||
|
|
||||||
|
wait_for_migration_status(from, "failed",
|
||||||
|
(const char * []) { "completed", NULL });
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Not waiting for the destination because it never started
|
||||||
|
* migration.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_cancel_src_after_cancelled(QTestState *from, QTestState *to,
|
||||||
|
const char *uri, const char *phase)
|
||||||
|
{
|
||||||
|
migrate_incoming_qmp(to, uri, NULL, "{ 'exit-on-error': false }");
|
||||||
|
|
||||||
|
wait_for_serial("src_serial");
|
||||||
|
migrate_ensure_converge(from);
|
||||||
|
|
||||||
|
migrate_qmp(from, to, uri, NULL, "{}");
|
||||||
|
|
||||||
|
/* To move to cancelled/cancelling */
|
||||||
|
migrate_cancel(from);
|
||||||
|
migration_event_wait(from, phase);
|
||||||
|
|
||||||
|
/* The migrate_cancel under test */
|
||||||
|
migrate_cancel(from);
|
||||||
|
|
||||||
|
wait_for_migration_status(from, "cancelled",
|
||||||
|
(const char * []) { "completed", NULL });
|
||||||
|
|
||||||
|
wait_for_migration_status(to, "failed",
|
||||||
|
(const char * []) { "completed", NULL });
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_cancel_src_after_complete(QTestState *from, QTestState *to,
|
||||||
|
const char *uri, const char *phase)
|
||||||
|
{
|
||||||
|
migrate_incoming_qmp(to, uri, NULL, "{ 'exit-on-error': false }");
|
||||||
|
|
||||||
|
wait_for_serial("src_serial");
|
||||||
|
migrate_ensure_converge(from);
|
||||||
|
|
||||||
|
migrate_qmp(from, to, uri, NULL, "{}");
|
||||||
|
|
||||||
|
migration_event_wait(from, phase);
|
||||||
|
migrate_cancel(from);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* qmp_migrate_cancel() exits early if migration is not running
|
||||||
|
* anymore, the status will not change to cancelled.
|
||||||
|
*/
|
||||||
|
wait_for_migration_complete(from);
|
||||||
|
wait_for_migration_complete(to);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_cancel_src_after_none(QTestState *from, QTestState *to,
|
||||||
|
const char *uri, const char *phase)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Test that cancelling without a migration happening does not
|
||||||
|
* affect subsequent migrations
|
||||||
|
*/
|
||||||
|
migrate_cancel(to);
|
||||||
|
|
||||||
|
wait_for_serial("src_serial");
|
||||||
|
migrate_cancel(from);
|
||||||
|
|
||||||
|
migrate_incoming_qmp(to, uri, NULL, "{ 'exit-on-error': false }");
|
||||||
|
|
||||||
|
migrate_ensure_converge(from);
|
||||||
|
migrate_qmp(from, to, uri, NULL, "{}");
|
||||||
|
|
||||||
|
wait_for_migration_complete(from);
|
||||||
|
wait_for_migration_complete(to);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_cancel_src_pre_switchover(QTestState *from, QTestState *to,
|
||||||
|
const char *uri, const char *phase)
|
||||||
|
{
|
||||||
|
migrate_set_capability(from, "pause-before-switchover", true);
|
||||||
|
migrate_set_capability(to, "pause-before-switchover", true);
|
||||||
|
|
||||||
|
migrate_set_capability(from, "multifd", true);
|
||||||
|
migrate_set_capability(to, "multifd", true);
|
||||||
|
|
||||||
|
migrate_incoming_qmp(to, uri, NULL, "{ 'exit-on-error': false }");
|
||||||
|
|
||||||
|
wait_for_serial("src_serial");
|
||||||
|
migrate_ensure_converge(from);
|
||||||
|
|
||||||
|
migrate_qmp(from, to, uri, NULL, "{}");
|
||||||
|
|
||||||
|
migration_event_wait(from, phase);
|
||||||
|
migrate_cancel(from);
|
||||||
|
migration_event_wait(from, "cancelling");
|
||||||
|
|
||||||
|
wait_for_migration_status(from, "cancelled",
|
||||||
|
(const char * []) { "completed", NULL });
|
||||||
|
|
||||||
|
wait_for_migration_status(to, "failed",
|
||||||
|
(const char * []) { "completed", NULL });
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_cancel_src_after_status(void *opaque)
|
||||||
|
{
|
||||||
|
const char *test_path = opaque;
|
||||||
|
g_autofree char *phase = g_path_get_basename(test_path);
|
||||||
|
g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
|
||||||
|
QTestState *from, *to;
|
||||||
|
MigrateStart args = {
|
||||||
|
.hide_stderr = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (migrate_start(&from, &to, "defer", &args)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_str_equal(phase, "cancelling") ||
|
||||||
|
g_str_equal(phase, "cancelled")) {
|
||||||
|
test_cancel_src_after_cancelled(from, to, uri, phase);
|
||||||
|
|
||||||
|
} else if (g_str_equal(phase, "completed")) {
|
||||||
|
test_cancel_src_after_complete(from, to, uri, phase);
|
||||||
|
|
||||||
|
} else if (g_str_equal(phase, "failed")) {
|
||||||
|
test_cancel_src_after_failed(from, to, uri, phase);
|
||||||
|
|
||||||
|
} else if (g_str_equal(phase, "none")) {
|
||||||
|
test_cancel_src_after_none(from, to, uri, phase);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
/* any state that comes before pre-switchover */
|
||||||
|
test_cancel_src_pre_switchover(from, to, uri, phase);
|
||||||
|
}
|
||||||
|
|
||||||
|
migrate_end(from, to, false);
|
||||||
|
}
|
||||||
|
|
||||||
static void calc_dirty_rate(QTestState *who, uint64_t calc_time)
|
static void calc_dirty_rate(QTestState *who, uint64_t calc_time)
|
||||||
{
|
{
|
||||||
qtest_qmp_assert_success(who,
|
qtest_qmp_assert_success(who,
|
||||||
@ -1018,4 +1174,24 @@ void migration_test_add_precopy(MigrationTestEnv *env)
|
|||||||
test_vcpu_dirty_limit);
|
test_vcpu_dirty_limit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ensure new status don't go unnoticed */
|
||||||
|
assert(MIGRATION_STATUS__MAX == 15);
|
||||||
|
|
||||||
|
for (int i = MIGRATION_STATUS_NONE; i < MIGRATION_STATUS__MAX; i++) {
|
||||||
|
switch (i) {
|
||||||
|
case MIGRATION_STATUS_DEVICE: /* happens too fast */
|
||||||
|
case MIGRATION_STATUS_WAIT_UNPLUG: /* no support in tests */
|
||||||
|
case MIGRATION_STATUS_COLO: /* no support in tests */
|
||||||
|
case MIGRATION_STATUS_POSTCOPY_ACTIVE: /* postcopy can't be cancelled */
|
||||||
|
case MIGRATION_STATUS_POSTCOPY_PAUSED:
|
||||||
|
case MIGRATION_STATUS_POSTCOPY_RECOVER_SETUP:
|
||||||
|
case MIGRATION_STATUS_POSTCOPY_RECOVER:
|
||||||
|
continue;
|
||||||
|
default:
|
||||||
|
migration_test_add_suffix("/migration/cancel/src/after/",
|
||||||
|
MigrationStatus_str(i),
|
||||||
|
test_cancel_src_after_status);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user