diff options
| author | Peter Maydell <peter.maydell@linaro.org> | 2020-01-06 18:22:42 +0000 |
|---|---|---|
| committer | Peter Maydell <peter.maydell@linaro.org> | 2020-01-06 18:22:42 +0000 |
| commit | c4d1069c2563f70a5271af6e9e000add64e593be (patch) | |
| tree | 82aba26c5f030883ca62436b27eb8629a221fbed /tests/migration-helpers.c | |
| parent | f4d8cf148e43d942ef1202071e0cd66ce40322e0 (diff) | |
| parent | 586ca6ba3cd6d7a3a85ed5cdc4e53b1cd584b0c0 (diff) | |
| download | focaccia-qemu-c4d1069c2563f70a5271af6e9e000add64e593be.tar.gz focaccia-qemu-c4d1069c2563f70a5271af6e9e000add64e593be.zip | |
Merge remote-tracking branch 'remotes/elmarco/tags/dbus-vmstate7-pull-request' into staging
Add dbus-vmstate Hi, With external processes or helpers participating to the VM support, it becomes necessary to handle their migration. Various options exist to transfer their state: 1) as the VM memory, RAM or devices (we could say that's how vhost-user devices can be handled today, they are expected to restore from ring state) 2) other "vmstate" (as with TPM emulator state blobs) 3) left to be handled by management layer 1) is not practical, since an external processes may legitimatelly need arbitrary state date to back a device or a service, or may not even have an associated device. 2) needs ad-hoc code for each helper, but is simple and working 3) is complicated for management layer, QEMU has the migration timing The proposed "dbus-vmstate" object will connect to a given D-Bus address, and save/load from org.qemu.VMState1 owners on migration. Thus helpers can easily have their state migrated with QEMU, without implementing ad-hoc support (such as done for TPM emulation) D-Bus is ubiquitous on Linux (it is systemd IPC), and can be made to work on various other OSes. There are several implementations and good bindings for various languages. (the tests/dbus-vmstate-test.c is a good example of how simple the implementation of services can be, even in C) dbus-vmstate is put into use by the libvirt series "[PATCH 00/23] Use a slirp helper process". v2: - fix build with broken mingw-glib # gpg: Signature made Mon 06 Jan 2020 14:43:35 GMT # gpg: using RSA key 87A9BD933F87C606D276F62DDAE8E10975969CE5 # gpg: issuer "marcandre.lureau@redhat.com" # gpg: Good signature from "Marc-André Lureau <marcandre.lureau@redhat.com>" [full] # gpg: aka "Marc-André Lureau <marcandre.lureau@gmail.com>" [full] # Primary key fingerprint: 87A9 BD93 3F87 C606 D276 F62D DAE8 E109 7596 9CE5 * remotes/elmarco/tags/dbus-vmstate7-pull-request: tests: add dbus-vmstate-test tests: add migration-helpers unit dockerfiles: add dbus-daemon to some of latest distributions configure: add GDBUS_CODEGEN Add dbus-vmstate object util: add dbus helper unit docs: start a document to describe D-Bus usage vmstate: replace DeviceState with VMStateIf vmstate: add qom interface to get id Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'tests/migration-helpers.c')
| -rw-r--r-- | tests/migration-helpers.c | 167 |
1 files changed, 167 insertions, 0 deletions
diff --git a/tests/migration-helpers.c b/tests/migration-helpers.c new file mode 100644 index 0000000000..516093b39a --- /dev/null +++ b/tests/migration-helpers.c @@ -0,0 +1,167 @@ +/* + * QTest migration helpers + * + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "qapi/qmp/qjson.h" + +#include "migration-helpers.h" + +bool got_stop; + +static void stop_cb(void *opaque, const char *name, QDict *data) +{ + if (!strcmp(name, "STOP")) { + got_stop = true; + } +} + +/* + * Events can get in the way of responses we are actually waiting for. + */ +QDict *wait_command_fd(QTestState *who, int fd, const char *command, ...) +{ + va_list ap; + + va_start(ap, command); + qtest_qmp_vsend_fds(who, &fd, 1, command, ap); + va_end(ap); + + return qtest_qmp_receive_success(who, stop_cb, NULL); +} + +/* + * Events can get in the way of responses we are actually waiting for. + */ +QDict *wait_command(QTestState *who, const char *command, ...) +{ + va_list ap; + + va_start(ap, command); + qtest_qmp_vsend(who, command, ap); + va_end(ap); + + return qtest_qmp_receive_success(who, stop_cb, NULL); +} + +/* + * Send QMP command "migrate". + * Arguments are built from @fmt... (formatted like + * qobject_from_jsonf_nofail()) with "uri": @uri spliced in. + */ +void migrate_qmp(QTestState *who, const char *uri, const char *fmt, ...) +{ + va_list ap; + QDict *args, *rsp; + + va_start(ap, fmt); + args = qdict_from_vjsonf_nofail(fmt, ap); + va_end(ap); + + g_assert(!qdict_haskey(args, "uri")); + qdict_put_str(args, "uri", uri); + + rsp = qtest_qmp(who, "{ 'execute': 'migrate', 'arguments': %p}", args); + + g_assert(qdict_haskey(rsp, "return")); + qobject_unref(rsp); +} + +/* + * Note: caller is responsible to free the returned object via + * qobject_unref() after use + */ +QDict *migrate_query(QTestState *who) +{ + return wait_command(who, "{ 'execute': 'query-migrate' }"); +} + +/* + * Note: caller is responsible to free the returned object via + * g_free() after use + */ +static gchar *migrate_query_status(QTestState *who) +{ + QDict *rsp_return = migrate_query(who); + gchar *status = g_strdup(qdict_get_str(rsp_return, "status")); + + g_assert(status); + qobject_unref(rsp_return); + + return status; +} + +static bool check_migration_status(QTestState *who, const char *goal, + const char **ungoals) +{ + bool ready; + char *current_status; + const char **ungoal; + + current_status = migrate_query_status(who); + ready = strcmp(current_status, goal) == 0; + if (!ungoals) { + g_assert_cmpstr(current_status, !=, "failed"); + /* + * If looking for a state other than completed, + * completion of migration would cause the test to + * hang. + */ + if (strcmp(goal, "completed") != 0) { + g_assert_cmpstr(current_status, !=, "completed"); + } + } else { + for (ungoal = ungoals; *ungoal; ungoal++) { + g_assert_cmpstr(current_status, !=, *ungoal); + } + } + g_free(current_status); + return ready; +} + +void wait_for_migration_status(QTestState *who, + const char *goal, const char **ungoals) +{ + while (!check_migration_status(who, goal, ungoals)) { + usleep(1000); + } +} + +void wait_for_migration_complete(QTestState *who) +{ + wait_for_migration_status(who, "completed", NULL); +} + +void wait_for_migration_fail(QTestState *from, bool allow_active) +{ + QDict *rsp_return; + char *status; + bool failed; + + do { + status = migrate_query_status(from); + bool result = !strcmp(status, "setup") || !strcmp(status, "failed") || + (allow_active && !strcmp(status, "active")); + if (!result) { + fprintf(stderr, "%s: unexpected status status=%s allow_active=%d\n", + __func__, status, allow_active); + } + g_assert(result); + failed = !strcmp(status, "failed"); + g_free(status); + } while (!failed); + + /* Is the machine currently running? */ + rsp_return = wait_command(from, "{ 'execute': 'query-status' }"); + g_assert(qdict_haskey(rsp_return, "running")); + g_assert(qdict_get_bool(rsp_return, "running")); + qobject_unref(rsp_return); +} |