11e8a1faeSThomas Huth /*
21e8a1faeSThomas Huth  * QTest migration helpers
31e8a1faeSThomas Huth  *
41e8a1faeSThomas Huth  * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates
51e8a1faeSThomas Huth  *   based on the vhost-user-test.c that is:
61e8a1faeSThomas Huth  *      Copyright (c) 2014 Virtual Open Systems Sarl.
71e8a1faeSThomas Huth  *
81e8a1faeSThomas Huth  * This work is licensed under the terms of the GNU GPL, version 2 or later.
91e8a1faeSThomas Huth  * See the COPYING file in the top-level directory.
101e8a1faeSThomas Huth  *
111e8a1faeSThomas Huth  */
121e8a1faeSThomas Huth 
131e8a1faeSThomas Huth #include "qemu/osdep.h"
14*6c6d2330SFabiano Rosas #include "qemu/ctype.h"
151e8a1faeSThomas Huth #include "qapi/qmp/qjson.h"
161e8a1faeSThomas Huth 
171e8a1faeSThomas Huth #include "migration-helpers.h"
181e8a1faeSThomas Huth 
19276dfd03SDaniel P. Berrangé /*
20276dfd03SDaniel P. Berrangé  * Number of seconds we wait when looking for migration
21276dfd03SDaniel P. Berrangé  * status changes, to avoid test suite hanging forever
22276dfd03SDaniel P. Berrangé  * when things go wrong. Needs to be higher enough to
23276dfd03SDaniel P. Berrangé  * avoid false positives on loaded hosts.
24276dfd03SDaniel P. Berrangé  */
25276dfd03SDaniel P. Berrangé #define MIGRATION_STATUS_WAIT_TIMEOUT 120
26276dfd03SDaniel P. Berrangé 
migrate_watch_for_stop(QTestState * who,const char * name,QDict * event,void * opaque)27cdf5ab55SDaniel P. Berrangé bool migrate_watch_for_stop(QTestState *who, const char *name,
28cdf5ab55SDaniel P. Berrangé                             QDict *event, void *opaque)
291e8a1faeSThomas Huth {
30cdf5ab55SDaniel P. Berrangé     bool *seen = opaque;
31cdf5ab55SDaniel P. Berrangé 
32cdf5ab55SDaniel P. Berrangé     if (g_str_equal(name, "STOP")) {
33cdf5ab55SDaniel P. Berrangé         *seen = true;
34cdf5ab55SDaniel P. Berrangé         return true;
351e8a1faeSThomas Huth     }
36cdf5ab55SDaniel P. Berrangé 
37cdf5ab55SDaniel P. Berrangé     return false;
381e8a1faeSThomas Huth }
391e8a1faeSThomas Huth 
migrate_watch_for_resume(QTestState * who,const char * name,QDict * event,void * opaque)40266ea334SDaniel P. Berrangé bool migrate_watch_for_resume(QTestState *who, const char *name,
41266ea334SDaniel P. Berrangé                               QDict *event, void *opaque)
42266ea334SDaniel P. Berrangé {
43266ea334SDaniel P. Berrangé     bool *seen = opaque;
44266ea334SDaniel P. Berrangé 
45266ea334SDaniel P. Berrangé     if (g_str_equal(name, "RESUME")) {
46266ea334SDaniel P. Berrangé         *seen = true;
47266ea334SDaniel P. Berrangé         return true;
48266ea334SDaniel P. Berrangé     }
49266ea334SDaniel P. Berrangé 
50266ea334SDaniel P. Berrangé     return false;
51266ea334SDaniel P. Berrangé }
52266ea334SDaniel P. Berrangé 
migrate_qmp_fail(QTestState * who,const char * uri,const char * fmt,...)535274274cSFabiano Rosas void migrate_qmp_fail(QTestState *who, const char *uri, const char *fmt, ...)
545274274cSFabiano Rosas {
555274274cSFabiano Rosas     va_list ap;
565274274cSFabiano Rosas     QDict *args, *err;
575274274cSFabiano Rosas 
585274274cSFabiano Rosas     va_start(ap, fmt);
595274274cSFabiano Rosas     args = qdict_from_vjsonf_nofail(fmt, ap);
605274274cSFabiano Rosas     va_end(ap);
615274274cSFabiano Rosas 
625274274cSFabiano Rosas     g_assert(!qdict_haskey(args, "uri"));
635274274cSFabiano Rosas     qdict_put_str(args, "uri", uri);
645274274cSFabiano Rosas 
655274274cSFabiano Rosas     err = qtest_qmp_assert_failure_ref(
665274274cSFabiano Rosas         who, "{ 'execute': 'migrate', 'arguments': %p}", args);
675274274cSFabiano Rosas 
685274274cSFabiano Rosas     g_assert(qdict_haskey(err, "desc"));
695274274cSFabiano Rosas 
705274274cSFabiano Rosas     qobject_unref(err);
715274274cSFabiano Rosas }
725274274cSFabiano Rosas 
731e8a1faeSThomas Huth /*
741e8a1faeSThomas Huth  * Send QMP command "migrate".
751e8a1faeSThomas Huth  * Arguments are built from @fmt... (formatted like
761e8a1faeSThomas Huth  * qobject_from_jsonf_nofail()) with "uri": @uri spliced in.
771e8a1faeSThomas Huth  */
migrate_qmp(QTestState * who,const char * uri,const char * fmt,...)781e8a1faeSThomas Huth void migrate_qmp(QTestState *who, const char *uri, const char *fmt, ...)
791e8a1faeSThomas Huth {
801e8a1faeSThomas Huth     va_list ap;
8111936f0eSDaniel P. Berrangé     QDict *args;
821e8a1faeSThomas Huth 
831e8a1faeSThomas Huth     va_start(ap, fmt);
841e8a1faeSThomas Huth     args = qdict_from_vjsonf_nofail(fmt, ap);
851e8a1faeSThomas Huth     va_end(ap);
861e8a1faeSThomas Huth 
871e8a1faeSThomas Huth     g_assert(!qdict_haskey(args, "uri"));
881e8a1faeSThomas Huth     qdict_put_str(args, "uri", uri);
891e8a1faeSThomas Huth 
9011936f0eSDaniel P. Berrangé     qtest_qmp_assert_success(who,
9111936f0eSDaniel P. Berrangé                              "{ 'execute': 'migrate', 'arguments': %p}", args);
921e8a1faeSThomas Huth }
931e8a1faeSThomas Huth 
migrate_set_capability(QTestState * who,const char * capability,bool value)949d479290SFabiano Rosas void migrate_set_capability(QTestState *who, const char *capability,
959d479290SFabiano Rosas                             bool value)
969d479290SFabiano Rosas {
979d479290SFabiano Rosas     qtest_qmp_assert_success(who,
989d479290SFabiano Rosas                              "{ 'execute': 'migrate-set-capabilities',"
999d479290SFabiano Rosas                              "'arguments': { "
1009d479290SFabiano Rosas                              "'capabilities': [ { "
1019d479290SFabiano Rosas                              "'capability': %s, 'state': %i } ] } }",
1029d479290SFabiano Rosas                              capability, value);
1039d479290SFabiano Rosas }
1049d479290SFabiano Rosas 
migrate_incoming_qmp(QTestState * to,const char * uri,const char * fmt,...)10528fa97e0SFabiano Rosas void migrate_incoming_qmp(QTestState *to, const char *uri, const char *fmt, ...)
10628fa97e0SFabiano Rosas {
10728fa97e0SFabiano Rosas     va_list ap;
10828fa97e0SFabiano Rosas     QDict *args, *rsp, *data;
10928fa97e0SFabiano Rosas 
11028fa97e0SFabiano Rosas     va_start(ap, fmt);
11128fa97e0SFabiano Rosas     args = qdict_from_vjsonf_nofail(fmt, ap);
11228fa97e0SFabiano Rosas     va_end(ap);
11328fa97e0SFabiano Rosas 
11428fa97e0SFabiano Rosas     g_assert(!qdict_haskey(args, "uri"));
11528fa97e0SFabiano Rosas     qdict_put_str(args, "uri", uri);
11628fa97e0SFabiano Rosas 
11728fa97e0SFabiano Rosas     migrate_set_capability(to, "events", true);
11828fa97e0SFabiano Rosas 
11928fa97e0SFabiano Rosas     rsp = qtest_qmp(to, "{ 'execute': 'migrate-incoming', 'arguments': %p}",
12028fa97e0SFabiano Rosas                     args);
12128fa97e0SFabiano Rosas     g_assert(qdict_haskey(rsp, "return"));
12228fa97e0SFabiano Rosas     qobject_unref(rsp);
12328fa97e0SFabiano Rosas 
12428fa97e0SFabiano Rosas     rsp = qtest_qmp_eventwait_ref(to, "MIGRATION");
12528fa97e0SFabiano Rosas     g_assert(qdict_haskey(rsp, "data"));
12628fa97e0SFabiano Rosas 
12728fa97e0SFabiano Rosas     data = qdict_get_qdict(rsp, "data");
12828fa97e0SFabiano Rosas     g_assert(qdict_haskey(data, "status"));
12928fa97e0SFabiano Rosas     g_assert_cmpstr(qdict_get_str(data, "status"), ==, "setup");
13028fa97e0SFabiano Rosas 
13128fa97e0SFabiano Rosas     qobject_unref(rsp);
13228fa97e0SFabiano Rosas }
13328fa97e0SFabiano Rosas 
1341e8a1faeSThomas Huth /*
1351e8a1faeSThomas Huth  * Note: caller is responsible to free the returned object via
1361e8a1faeSThomas Huth  * qobject_unref() after use
1371e8a1faeSThomas Huth  */
migrate_query(QTestState * who)1381e8a1faeSThomas Huth QDict *migrate_query(QTestState *who)
1391e8a1faeSThomas Huth {
140aca04069SDaniel P. Berrangé     return qtest_qmp_assert_success_ref(who, "{ 'execute': 'query-migrate' }");
1411e8a1faeSThomas Huth }
1421e8a1faeSThomas Huth 
migrate_query_not_failed(QTestState * who)143fd3540adSDaniel P. Berrangé QDict *migrate_query_not_failed(QTestState *who)
144fd3540adSDaniel P. Berrangé {
145fd3540adSDaniel P. Berrangé     const char *status;
146fd3540adSDaniel P. Berrangé     QDict *rsp = migrate_query(who);
147fd3540adSDaniel P. Berrangé     status = qdict_get_str(rsp, "status");
148fd3540adSDaniel P. Berrangé     if (g_str_equal(status, "failed")) {
149fd3540adSDaniel P. Berrangé         g_printerr("query-migrate shows failed migration: %s\n",
150fd3540adSDaniel P. Berrangé                    qdict_get_str(rsp, "error-desc"));
151fd3540adSDaniel P. Berrangé     }
152fd3540adSDaniel P. Berrangé     g_assert(!g_str_equal(status, "failed"));
153fd3540adSDaniel P. Berrangé     return rsp;
154fd3540adSDaniel P. Berrangé }
155fd3540adSDaniel P. Berrangé 
1561e8a1faeSThomas Huth /*
1571e8a1faeSThomas Huth  * Note: caller is responsible to free the returned object via
1581e8a1faeSThomas Huth  * g_free() after use
1591e8a1faeSThomas Huth  */
migrate_query_status(QTestState * who)1601e8a1faeSThomas Huth static gchar *migrate_query_status(QTestState *who)
1611e8a1faeSThomas Huth {
1621e8a1faeSThomas Huth     QDict *rsp_return = migrate_query(who);
1631e8a1faeSThomas Huth     gchar *status = g_strdup(qdict_get_str(rsp_return, "status"));
1641e8a1faeSThomas Huth 
1651e8a1faeSThomas Huth     g_assert(status);
1661e8a1faeSThomas Huth     qobject_unref(rsp_return);
1671e8a1faeSThomas Huth 
1681e8a1faeSThomas Huth     return status;
1691e8a1faeSThomas Huth }
1701e8a1faeSThomas Huth 
check_migration_status(QTestState * who,const char * goal,const char ** ungoals)1711e8a1faeSThomas Huth static bool check_migration_status(QTestState *who, const char *goal,
1721e8a1faeSThomas Huth                                    const char **ungoals)
1731e8a1faeSThomas Huth {
1741e8a1faeSThomas Huth     bool ready;
1751e8a1faeSThomas Huth     char *current_status;
1761e8a1faeSThomas Huth     const char **ungoal;
1771e8a1faeSThomas Huth 
1781e8a1faeSThomas Huth     current_status = migrate_query_status(who);
1791e8a1faeSThomas Huth     ready = strcmp(current_status, goal) == 0;
1801e8a1faeSThomas Huth     if (!ungoals) {
1811e8a1faeSThomas Huth         g_assert_cmpstr(current_status, !=, "failed");
1821e8a1faeSThomas Huth         /*
1831e8a1faeSThomas Huth          * If looking for a state other than completed,
1841e8a1faeSThomas Huth          * completion of migration would cause the test to
1851e8a1faeSThomas Huth          * hang.
1861e8a1faeSThomas Huth          */
1871e8a1faeSThomas Huth         if (strcmp(goal, "completed") != 0) {
1881e8a1faeSThomas Huth             g_assert_cmpstr(current_status, !=, "completed");
1891e8a1faeSThomas Huth         }
1901e8a1faeSThomas Huth     } else {
1911e8a1faeSThomas Huth         for (ungoal = ungoals; *ungoal; ungoal++) {
1921e8a1faeSThomas Huth             g_assert_cmpstr(current_status, !=,  *ungoal);
1931e8a1faeSThomas Huth         }
1941e8a1faeSThomas Huth     }
1951e8a1faeSThomas Huth     g_free(current_status);
1961e8a1faeSThomas Huth     return ready;
1971e8a1faeSThomas Huth }
1981e8a1faeSThomas Huth 
wait_for_migration_status(QTestState * who,const char * goal,const char ** ungoals)1991e8a1faeSThomas Huth void wait_for_migration_status(QTestState *who,
2001e8a1faeSThomas Huth                                const char *goal, const char **ungoals)
2011e8a1faeSThomas Huth {
202276dfd03SDaniel P. Berrangé     g_test_timer_start();
2031e8a1faeSThomas Huth     while (!check_migration_status(who, goal, ungoals)) {
2041e8a1faeSThomas Huth         usleep(1000);
205276dfd03SDaniel P. Berrangé 
206276dfd03SDaniel P. Berrangé         g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT);
2071e8a1faeSThomas Huth     }
2081e8a1faeSThomas Huth }
2091e8a1faeSThomas Huth 
wait_for_migration_complete(QTestState * who)2101e8a1faeSThomas Huth void wait_for_migration_complete(QTestState *who)
2111e8a1faeSThomas Huth {
2121e8a1faeSThomas Huth     wait_for_migration_status(who, "completed", NULL);
2131e8a1faeSThomas Huth }
2141e8a1faeSThomas Huth 
wait_for_migration_fail(QTestState * from,bool allow_active)2151e8a1faeSThomas Huth void wait_for_migration_fail(QTestState *from, bool allow_active)
2161e8a1faeSThomas Huth {
217276dfd03SDaniel P. Berrangé     g_test_timer_start();
2181e8a1faeSThomas Huth     QDict *rsp_return;
2191e8a1faeSThomas Huth     char *status;
2201e8a1faeSThomas Huth     bool failed;
2211e8a1faeSThomas Huth 
2221e8a1faeSThomas Huth     do {
2231e8a1faeSThomas Huth         status = migrate_query_status(from);
2241e8a1faeSThomas Huth         bool result = !strcmp(status, "setup") || !strcmp(status, "failed") ||
2251e8a1faeSThomas Huth             (allow_active && !strcmp(status, "active"));
2261e8a1faeSThomas Huth         if (!result) {
2271e8a1faeSThomas Huth             fprintf(stderr, "%s: unexpected status status=%s allow_active=%d\n",
2281e8a1faeSThomas Huth                     __func__, status, allow_active);
2291e8a1faeSThomas Huth         }
2301e8a1faeSThomas Huth         g_assert(result);
2311e8a1faeSThomas Huth         failed = !strcmp(status, "failed");
2321e8a1faeSThomas Huth         g_free(status);
233276dfd03SDaniel P. Berrangé 
234276dfd03SDaniel P. Berrangé         g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT);
2351e8a1faeSThomas Huth     } while (!failed);
2361e8a1faeSThomas Huth 
2371e8a1faeSThomas Huth     /* Is the machine currently running? */
238aca04069SDaniel P. Berrangé     rsp_return = qtest_qmp_assert_success_ref(from,
239aca04069SDaniel P. Berrangé                                               "{ 'execute': 'query-status' }");
2401e8a1faeSThomas Huth     g_assert(qdict_haskey(rsp_return, "running"));
2411e8a1faeSThomas Huth     g_assert(qdict_get_bool(rsp_return, "running"));
2421e8a1faeSThomas Huth     qobject_unref(rsp_return);
2431e8a1faeSThomas Huth }
244dcf389cbSFabiano Rosas 
find_common_machine_version(const char * mtype,const char * var1,const char * var2)245dcf389cbSFabiano Rosas char *find_common_machine_version(const char *mtype, const char *var1,
246dcf389cbSFabiano Rosas                                   const char *var2)
247dcf389cbSFabiano Rosas {
248dcf389cbSFabiano Rosas     g_autofree char *type1 = qtest_resolve_machine_alias(var1, mtype);
249dcf389cbSFabiano Rosas     g_autofree char *type2 = qtest_resolve_machine_alias(var2, mtype);
250dcf389cbSFabiano Rosas 
251dcf389cbSFabiano Rosas     g_assert(type1 && type2);
252dcf389cbSFabiano Rosas 
253dcf389cbSFabiano Rosas     if (g_str_equal(type1, type2)) {
254dcf389cbSFabiano Rosas         /* either can be used */
255dcf389cbSFabiano Rosas         return g_strdup(type1);
256dcf389cbSFabiano Rosas     }
257dcf389cbSFabiano Rosas 
258dcf389cbSFabiano Rosas     if (qtest_has_machine_with_env(var2, type1)) {
259dcf389cbSFabiano Rosas         return g_strdup(type1);
260dcf389cbSFabiano Rosas     }
261dcf389cbSFabiano Rosas 
262dcf389cbSFabiano Rosas     if (qtest_has_machine_with_env(var1, type2)) {
263dcf389cbSFabiano Rosas         return g_strdup(type2);
264dcf389cbSFabiano Rosas     }
265dcf389cbSFabiano Rosas 
266dcf389cbSFabiano Rosas     g_test_message("No common machine version for machine type '%s' between "
267dcf389cbSFabiano Rosas                    "binaries %s and %s", mtype, getenv(var1), getenv(var2));
268dcf389cbSFabiano Rosas     g_assert_not_reached();
269dcf389cbSFabiano Rosas }
270*6c6d2330SFabiano Rosas 
resolve_machine_version(const char * alias,const char * var1,const char * var2)271*6c6d2330SFabiano Rosas char *resolve_machine_version(const char *alias, const char *var1,
272*6c6d2330SFabiano Rosas                               const char *var2)
273*6c6d2330SFabiano Rosas {
274*6c6d2330SFabiano Rosas     const char *mname = g_getenv("QTEST_QEMU_MACHINE_TYPE");
275*6c6d2330SFabiano Rosas     g_autofree char *machine_name = NULL;
276*6c6d2330SFabiano Rosas 
277*6c6d2330SFabiano Rosas     if (mname) {
278*6c6d2330SFabiano Rosas         const char *dash = strrchr(mname, '-');
279*6c6d2330SFabiano Rosas         const char *dot = strrchr(mname, '.');
280*6c6d2330SFabiano Rosas 
281*6c6d2330SFabiano Rosas         machine_name = g_strdup(mname);
282*6c6d2330SFabiano Rosas 
283*6c6d2330SFabiano Rosas         if (dash && dot) {
284*6c6d2330SFabiano Rosas             assert(qtest_has_machine(machine_name));
285*6c6d2330SFabiano Rosas             return g_steal_pointer(&machine_name);
286*6c6d2330SFabiano Rosas         }
287*6c6d2330SFabiano Rosas         /* else: probably an alias, let it be resolved below */
288*6c6d2330SFabiano Rosas     } else {
289*6c6d2330SFabiano Rosas         /* use the hardcoded alias */
290*6c6d2330SFabiano Rosas         machine_name = g_strdup(alias);
291*6c6d2330SFabiano Rosas     }
292*6c6d2330SFabiano Rosas 
293*6c6d2330SFabiano Rosas     return find_common_machine_version(machine_name, var1, var2);
294*6c6d2330SFabiano Rosas }
295