]>
Commit | Line | Data |
---|---|---|
43cd2098 | 1 | /* |
e2f3f221 | 2 | * blockdev.c test cases |
43cd2098 | 3 | * |
e2f3f221 | 4 | * Copyright (C) 2013-2014 Red Hat Inc. |
43cd2098 SH |
5 | * |
6 | * Authors: | |
7 | * Stefan Hajnoczi <stefanha@redhat.com> | |
8 | * | |
9 | * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. | |
10 | * See the COPYING.LIB file in the top-level directory. | |
11 | */ | |
12 | ||
681c28a3 | 13 | #include "qemu/osdep.h" |
a2ce7dbd | 14 | #include "libqos/libqtest.h" |
2f84a92e | 15 | #include "libqos/virtio.h" |
452fcdbc | 16 | #include "qapi/qmp/qdict.h" |
9a613ddc PB |
17 | #include "qapi/qmp/qlist.h" |
18 | ||
d8a18da5 | 19 | static bool look_for_drive0(QTestState *qts, const char *command, const char *key) |
9a613ddc PB |
20 | { |
21 | QDict *response; | |
22 | QList *ret; | |
23 | QListEntry *entry; | |
24 | bool found; | |
25 | ||
d8a18da5 | 26 | response = qtest_qmp(qts, "{'execute': %s}", command); |
9a613ddc PB |
27 | g_assert(response && qdict_haskey(response, "return")); |
28 | ret = qdict_get_qlist(response, "return"); | |
29 | ||
30 | found = false; | |
31 | QLIST_FOREACH_ENTRY(ret, entry) { | |
32 | QDict *entry_dict = qobject_to(QDict, entry->value); | |
d8a18da5 | 33 | if (!strcmp(qdict_get_str(entry_dict, key), "drive0")) { |
9a613ddc PB |
34 | found = true; |
35 | break; | |
36 | } | |
37 | } | |
38 | ||
39 | qobject_unref(response); | |
40 | return found; | |
41 | } | |
43cd2098 | 42 | |
d8a18da5 PB |
43 | static bool has_drive(QTestState *qts) |
44 | { | |
45 | return look_for_drive0(qts, "query-block", "device"); | |
46 | } | |
47 | ||
48 | static bool has_blockdev(QTestState *qts) | |
49 | { | |
50 | return look_for_drive0(qts, "query-named-block-nodes", "node-name"); | |
51 | } | |
52 | ||
53 | static void blockdev_add_with_media(QTestState *qts) | |
54 | { | |
55 | QDict *response; | |
56 | ||
57 | response = qtest_qmp(qts, | |
58 | "{ 'execute': 'blockdev-add'," | |
59 | " 'arguments': {" | |
60 | " 'driver': 'raw'," | |
61 | " 'node-name': 'drive0'," | |
62 | " 'file': {" | |
63 | " 'driver': 'null-co'," | |
64 | " 'read-zeroes': true" | |
65 | " }" | |
66 | " }" | |
67 | "}"); | |
68 | ||
69 | g_assert(response); | |
70 | g_assert(qdict_haskey(response, "return")); | |
71 | qobject_unref(response); | |
72 | g_assert(has_blockdev(qts)); | |
73 | } | |
74 | ||
a771729c | 75 | static void drive_add(QTestState *qts) |
e2f3f221 | 76 | { |
a771729c | 77 | char *resp = qtest_hmp(qts, "drive_add 0 if=none,id=drive0"); |
e2f3f221 | 78 | |
5fb48d96 | 79 | g_assert_cmpstr(resp, ==, "OK\r\n"); |
9a613ddc | 80 | g_assert(has_drive(qts)); |
5fb48d96 | 81 | g_free(resp); |
2eea5cd4 MA |
82 | } |
83 | ||
d8a18da5 PB |
84 | static void drive_add_with_media(QTestState *qts) |
85 | { | |
86 | char *resp = qtest_hmp(qts, | |
87 | "drive_add 0 if=none,id=drive0,file=null-co://," | |
88 | "file.read-zeroes=on,format=raw"); | |
89 | ||
90 | g_assert_cmpstr(resp, ==, "OK\r\n"); | |
91 | g_assert(has_drive(qts)); | |
92 | g_free(resp); | |
93 | } | |
94 | ||
a771729c | 95 | static void drive_del(QTestState *qts) |
2eea5cd4 | 96 | { |
9a613ddc | 97 | char *resp; |
e2f3f221 | 98 | |
9a613ddc PB |
99 | g_assert(has_drive(qts)); |
100 | resp = qtest_hmp(qts, "drive_del drive0"); | |
5fb48d96 | 101 | g_assert_cmpstr(resp, ==, ""); |
9a613ddc | 102 | g_assert(!has_drive(qts)); |
5fb48d96 | 103 | g_free(resp); |
2eea5cd4 MA |
104 | } |
105 | ||
d8a18da5 PB |
106 | /* |
107 | * qvirtio_get_dev_type: | |
108 | * Returns: the preferred virtio bus/device type for the current architecture. | |
109 | * TODO: delete this | |
110 | */ | |
111 | static const char *qvirtio_get_dev_type(void) | |
112 | { | |
113 | const char *arch = qtest_get_arch(); | |
114 | ||
115 | if (g_str_equal(arch, "arm") || g_str_equal(arch, "aarch64")) { | |
116 | return "device"; /* for virtio-mmio */ | |
117 | } else if (g_str_equal(arch, "s390x")) { | |
118 | return "ccw"; | |
119 | } else { | |
120 | return "pci"; | |
121 | } | |
122 | } | |
123 | ||
124 | static void device_add(QTestState *qts) | |
125 | { | |
126 | QDict *response; | |
127 | char driver[32]; | |
128 | snprintf(driver, sizeof(driver), "virtio-blk-%s", | |
129 | qvirtio_get_dev_type()); | |
130 | ||
131 | response = qtest_qmp(qts, "{'execute': 'device_add'," | |
132 | " 'arguments': {" | |
133 | " 'driver': %s," | |
134 | " 'drive': 'drive0'," | |
135 | " 'id': 'dev0'" | |
136 | "}}", driver); | |
137 | g_assert(response); | |
138 | g_assert(qdict_haskey(response, "return")); | |
139 | qobject_unref(response); | |
140 | } | |
141 | ||
142 | static void device_del(QTestState *qts, bool and_reset) | |
767c86d3 MA |
143 | { |
144 | QDict *response; | |
145 | ||
bb1a5b97 | 146 | response = qtest_qmp(qts, "{'execute': 'device_del'," |
767c86d3 | 147 | " 'arguments': { 'id': 'dev0' } }"); |
767c86d3 MA |
148 | g_assert(response); |
149 | g_assert(qdict_haskey(response, "return")); | |
cb3e7f08 | 150 | qobject_unref(response); |
bb1a5b97 | 151 | |
d8a18da5 PB |
152 | if (and_reset) { |
153 | response = qtest_qmp(qts, "{'execute': 'system_reset' }"); | |
154 | g_assert(response); | |
155 | g_assert(qdict_haskey(response, "return")); | |
156 | qobject_unref(response); | |
157 | } | |
158 | ||
bb1a5b97 | 159 | qtest_qmp_eventwait(qts, "DEVICE_DELETED"); |
767c86d3 MA |
160 | } |
161 | ||
2eea5cd4 MA |
162 | static void test_drive_without_dev(void) |
163 | { | |
a771729c TH |
164 | QTestState *qts; |
165 | ||
2eea5cd4 | 166 | /* Start with an empty drive */ |
a771729c | 167 | qts = qtest_init("-drive if=none,id=drive0"); |
2eea5cd4 MA |
168 | |
169 | /* Delete the drive */ | |
a771729c | 170 | drive_del(qts); |
e2f3f221 MA |
171 | |
172 | /* Ensure re-adding the drive works - there should be no duplicate ID error | |
173 | * because the old drive must be gone. | |
174 | */ | |
a771729c | 175 | drive_add(qts); |
e2f3f221 | 176 | |
a771729c | 177 | qtest_quit(qts); |
e2f3f221 MA |
178 | } |
179 | ||
180 | static void test_after_failed_device_add(void) | |
43cd2098 | 181 | { |
83273e84 | 182 | char driver[32]; |
43cd2098 | 183 | QDict *response; |
a771729c | 184 | QTestState *qts; |
43cd2098 | 185 | |
83273e84 MA |
186 | snprintf(driver, sizeof(driver), "virtio-blk-%s", |
187 | qvirtio_get_dev_type()); | |
188 | ||
a771729c | 189 | qts = qtest_init("-drive if=none,id=drive0"); |
43cd2098 | 190 | |
2f84a92e | 191 | /* Make device_add fail. If this leaks the virtio-blk device then a |
43cd2098 SH |
192 | * reference to drive0 will also be held (via qdev properties). |
193 | */ | |
a771729c TH |
194 | response = qtest_qmp(qts, "{'execute': 'device_add'," |
195 | " 'arguments': {" | |
196 | " 'driver': %s," | |
197 | " 'drive': 'drive0'" | |
198 | "}}", driver); | |
43cd2098 | 199 | g_assert(response); |
3bc1b8ee | 200 | qmp_expect_error_and_unref(response, "GenericError"); |
43cd2098 SH |
201 | |
202 | /* Delete the drive */ | |
a771729c | 203 | drive_del(qts); |
43cd2098 SH |
204 | |
205 | /* Try to re-add the drive. This fails with duplicate IDs if a leaked | |
2f84a92e | 206 | * virtio-blk device exists that holds a reference to the old drive0. |
43cd2098 | 207 | */ |
a771729c | 208 | drive_add(qts); |
43cd2098 | 209 | |
a771729c | 210 | qtest_quit(qts); |
43cd2098 SH |
211 | } |
212 | ||
767c86d3 MA |
213 | static void test_drive_del_device_del(void) |
214 | { | |
a771729c | 215 | QTestState *qts; |
2f84a92e | 216 | |
767c86d3 | 217 | /* Start with a drive used by a device that unplugs instantaneously */ |
ca1ef1e6 AS |
218 | qts = qtest_initf("-drive if=none,id=drive0,file=null-co://," |
219 | "file.read-zeroes=on,format=raw" | |
a771729c TH |
220 | " -device virtio-scsi-%s" |
221 | " -device scsi-hd,drive=drive0,id=dev0", | |
222 | qvirtio_get_dev_type()); | |
767c86d3 MA |
223 | |
224 | /* | |
225 | * Delete the drive, and then the device | |
226 | * Doing it in this order takes notoriously tricky special paths | |
227 | */ | |
a771729c | 228 | drive_del(qts); |
d8a18da5 | 229 | device_del(qts, false); |
9a613ddc | 230 | g_assert(!has_drive(qts)); |
767c86d3 | 231 | |
a771729c | 232 | qtest_quit(qts); |
767c86d3 MA |
233 | } |
234 | ||
d8a18da5 PB |
235 | static void test_cli_device_del(void) |
236 | { | |
237 | QTestState *qts; | |
238 | ||
239 | /* | |
240 | * -drive/-device and device_del. Start with a drive used by a | |
241 | * device that unplugs after reset. | |
242 | */ | |
243 | qts = qtest_initf("-drive if=none,id=drive0,file=null-co://," | |
244 | "file.read-zeroes=on,format=raw" | |
245 | " -device virtio-blk-%s,drive=drive0,id=dev0", | |
246 | qvirtio_get_dev_type()); | |
247 | ||
248 | device_del(qts, true); | |
249 | g_assert(!has_drive(qts)); | |
250 | ||
251 | qtest_quit(qts); | |
252 | } | |
253 | ||
254 | static void test_empty_device_del(void) | |
255 | { | |
256 | QTestState *qts; | |
257 | ||
258 | /* device_del with no drive plugged. */ | |
259 | qts = qtest_initf("-device virtio-scsi-%s -device scsi-cd,id=dev0", | |
260 | qvirtio_get_dev_type()); | |
261 | ||
262 | device_del(qts, false); | |
263 | qtest_quit(qts); | |
264 | } | |
265 | ||
266 | static void test_device_add_and_del(void) | |
267 | { | |
268 | QTestState *qts; | |
269 | ||
270 | /* | |
271 | * -drive/device_add and device_del. Start with a drive used by a | |
272 | * device that unplugs after reset. | |
273 | */ | |
274 | qts = qtest_init("-drive if=none,id=drive0,file=null-co://," | |
275 | "file.read-zeroes=on,format=raw"); | |
276 | ||
277 | device_add(qts); | |
278 | device_del(qts, true); | |
279 | g_assert(!has_drive(qts)); | |
280 | ||
281 | qtest_quit(qts); | |
282 | } | |
283 | ||
284 | static void test_drive_add_device_add_and_del(void) | |
285 | { | |
286 | QTestState *qts; | |
287 | ||
288 | qts = qtest_init(""); | |
289 | ||
290 | /* | |
291 | * drive_add/device_add and device_del. The drive is used by a | |
292 | * device that unplugs after reset. | |
293 | */ | |
294 | drive_add_with_media(qts); | |
295 | device_add(qts); | |
296 | device_del(qts, true); | |
297 | g_assert(!has_drive(qts)); | |
298 | ||
299 | qtest_quit(qts); | |
300 | } | |
301 | ||
302 | static void test_blockdev_add_device_add_and_del(void) | |
303 | { | |
304 | QTestState *qts; | |
305 | ||
306 | qts = qtest_init(""); | |
307 | ||
308 | /* | |
309 | * blockdev_add/device_add and device_del. The it drive is used by a | |
310 | * device that unplugs after reset, but it doesn't go away. | |
311 | */ | |
312 | blockdev_add_with_media(qts); | |
313 | device_add(qts); | |
314 | device_del(qts, true); | |
315 | g_assert(has_blockdev(qts)); | |
316 | ||
317 | qtest_quit(qts); | |
318 | } | |
319 | ||
43cd2098 SH |
320 | int main(int argc, char **argv) |
321 | { | |
43cd2098 SH |
322 | g_test_init(&argc, &argv, NULL); |
323 | ||
e2f3f221 MA |
324 | qtest_add_func("/drive_del/without-dev", test_drive_without_dev); |
325 | ||
19e3d979 | 326 | if (qvirtio_get_dev_type() != NULL) { |
e2f3f221 MA |
327 | qtest_add_func("/drive_del/after_failed_device_add", |
328 | test_after_failed_device_add); | |
d8a18da5 | 329 | qtest_add_func("/drive_del/drive_del_device_del", |
767c86d3 | 330 | test_drive_del_device_del); |
d8a18da5 PB |
331 | qtest_add_func("/device_del/drive/cli_device", |
332 | test_cli_device_del); | |
333 | qtest_add_func("/device_del/drive/device_add", | |
334 | test_device_add_and_del); | |
335 | qtest_add_func("/device_del/drive/drive_add_device_add", | |
336 | test_drive_add_device_add_and_del); | |
337 | qtest_add_func("/device_del/empty", | |
338 | test_empty_device_del); | |
339 | qtest_add_func("/device_del/blockdev", | |
340 | test_blockdev_add_device_add_and_del); | |
e2f3f221 | 341 | } |
43cd2098 SH |
342 | |
343 | return g_test_run(); | |
344 | } |