xref: /openbmc/qemu/tests/qtest/migration-helpers.c (revision 5876d9b53fee5fc5a341fad1618cf3a9731206da)
1  /*
2   * QTest migration helpers
3   *
4   * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates
5   *   based on the vhost-user-test.c that is:
6   *      Copyright (c) 2014 Virtual Open Systems Sarl.
7   *
8   * This work is licensed under the terms of the GNU GPL, version 2 or later.
9   * See the COPYING file in the top-level directory.
10   *
11   */
12  
13  #include "qemu/osdep.h"
14  #include "qapi/qmp/qjson.h"
15  
16  #include "migration-helpers.h"
17  
18  bool got_stop;
19  
20  static void check_stop_event(QTestState *who)
21  {
22      QDict *event = qtest_qmp_event_ref(who, "STOP");
23      if (event) {
24          got_stop = true;
25          qobject_unref(event);
26      }
27  }
28  
29  /*
30   * Events can get in the way of responses we are actually waiting for.
31   */
32  QDict *wait_command_fd(QTestState *who, int fd, const char *command, ...)
33  {
34      va_list ap;
35      QDict *resp, *ret;
36  
37      va_start(ap, command);
38      qtest_qmp_vsend_fds(who, &fd, 1, command, ap);
39      va_end(ap);
40  
41      resp = qtest_qmp_receive(who);
42      check_stop_event(who);
43  
44      g_assert(!qdict_haskey(resp, "error"));
45      g_assert(qdict_haskey(resp, "return"));
46  
47      ret = qdict_get_qdict(resp, "return");
48      qobject_ref(ret);
49      qobject_unref(resp);
50  
51      return ret;
52  }
53  
54  /*
55   * Events can get in the way of responses we are actually waiting for.
56   */
57  QDict *wait_command(QTestState *who, const char *command, ...)
58  {
59      va_list ap;
60      QDict *resp, *ret;
61  
62      va_start(ap, command);
63      resp = qtest_vqmp(who, command, ap);
64      va_end(ap);
65  
66      check_stop_event(who);
67  
68      g_assert(!qdict_haskey(resp, "error"));
69      g_assert(qdict_haskey(resp, "return"));
70  
71      ret = qdict_get_qdict(resp, "return");
72      qobject_ref(ret);
73      qobject_unref(resp);
74  
75      return ret;
76  }
77  
78  /*
79   * Send QMP command "migrate".
80   * Arguments are built from @fmt... (formatted like
81   * qobject_from_jsonf_nofail()) with "uri": @uri spliced in.
82   */
83  void migrate_qmp(QTestState *who, const char *uri, const char *fmt, ...)
84  {
85      va_list ap;
86      QDict *args, *rsp;
87  
88      va_start(ap, fmt);
89      args = qdict_from_vjsonf_nofail(fmt, ap);
90      va_end(ap);
91  
92      g_assert(!qdict_haskey(args, "uri"));
93      qdict_put_str(args, "uri", uri);
94  
95      rsp = qtest_qmp(who, "{ 'execute': 'migrate', 'arguments': %p}", args);
96  
97      g_assert(qdict_haskey(rsp, "return"));
98      qobject_unref(rsp);
99  }
100  
101  /*
102   * Note: caller is responsible to free the returned object via
103   * qobject_unref() after use
104   */
105  QDict *migrate_query(QTestState *who)
106  {
107      return wait_command(who, "{ 'execute': 'query-migrate' }");
108  }
109  
110  QDict *migrate_query_not_failed(QTestState *who)
111  {
112      const char *status;
113      QDict *rsp = migrate_query(who);
114      status = qdict_get_str(rsp, "status");
115      if (g_str_equal(status, "failed")) {
116          g_printerr("query-migrate shows failed migration: %s\n",
117                     qdict_get_str(rsp, "error-desc"));
118      }
119      g_assert(!g_str_equal(status, "failed"));
120      return rsp;
121  }
122  
123  /*
124   * Note: caller is responsible to free the returned object via
125   * g_free() after use
126   */
127  static gchar *migrate_query_status(QTestState *who)
128  {
129      QDict *rsp_return = migrate_query(who);
130      gchar *status = g_strdup(qdict_get_str(rsp_return, "status"));
131  
132      g_assert(status);
133      qobject_unref(rsp_return);
134  
135      return status;
136  }
137  
138  static bool check_migration_status(QTestState *who, const char *goal,
139                                     const char **ungoals)
140  {
141      bool ready;
142      char *current_status;
143      const char **ungoal;
144  
145      current_status = migrate_query_status(who);
146      ready = strcmp(current_status, goal) == 0;
147      if (!ungoals) {
148          g_assert_cmpstr(current_status, !=, "failed");
149          /*
150           * If looking for a state other than completed,
151           * completion of migration would cause the test to
152           * hang.
153           */
154          if (strcmp(goal, "completed") != 0) {
155              g_assert_cmpstr(current_status, !=, "completed");
156          }
157      } else {
158          for (ungoal = ungoals; *ungoal; ungoal++) {
159              g_assert_cmpstr(current_status, !=,  *ungoal);
160          }
161      }
162      g_free(current_status);
163      return ready;
164  }
165  
166  void wait_for_migration_status(QTestState *who,
167                                 const char *goal, const char **ungoals)
168  {
169      while (!check_migration_status(who, goal, ungoals)) {
170          usleep(1000);
171      }
172  }
173  
174  void wait_for_migration_complete(QTestState *who)
175  {
176      wait_for_migration_status(who, "completed", NULL);
177  }
178  
179  void wait_for_migration_fail(QTestState *from, bool allow_active)
180  {
181      QDict *rsp_return;
182      char *status;
183      bool failed;
184  
185      do {
186          status = migrate_query_status(from);
187          bool result = !strcmp(status, "setup") || !strcmp(status, "failed") ||
188              (allow_active && !strcmp(status, "active"));
189          if (!result) {
190              fprintf(stderr, "%s: unexpected status status=%s allow_active=%d\n",
191                      __func__, status, allow_active);
192          }
193          g_assert(result);
194          failed = !strcmp(status, "failed");
195          g_free(status);
196      } while (!failed);
197  
198      /* Is the machine currently running? */
199      rsp_return = wait_command(from, "{ 'execute': 'query-status' }");
200      g_assert(qdict_haskey(rsp_return, "running"));
201      g_assert(qdict_get_bool(rsp_return, "running"));
202      qobject_unref(rsp_return);
203  }
204