]>
Commit | Line | Data |
---|---|---|
11a82d14 | 1 | #!/usr/bin/env bash |
9dd003a9 | 2 | # group: rw |
24575bfa KW |
3 | # |
4 | # Test exiting qemu while jobs are still running | |
5 | # | |
6 | # Copyright (C) 2017 Red Hat, Inc. | |
7 | # | |
8 | # This program is free software; you can redistribute it and/or modify | |
9 | # it under the terms of the GNU General Public License as published by | |
10 | # the Free Software Foundation; either version 2 of the License, or | |
11 | # (at your option) any later version. | |
12 | # | |
13 | # This program is distributed in the hope that it will be useful, | |
14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | # GNU General Public License for more details. | |
17 | # | |
18 | # You should have received a copy of the GNU General Public License | |
19 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
20 | # | |
21 | ||
22 | # creator | |
23 | owner=kwolf@redhat.com | |
24 | ||
25 | seq=`basename $0` | |
26 | echo "QA output created by $seq" | |
27 | ||
24575bfa KW |
28 | status=1 # failure is the default! |
29 | ||
24575bfa KW |
30 | _cleanup() |
31 | { | |
f91ecbd7 HR |
32 | _rm_test_img "${TEST_IMG}.mid" |
33 | _rm_test_img "${TEST_IMG}.copy" | |
24575bfa KW |
34 | _cleanup_test_img |
35 | _cleanup_qemu | |
ad6fe44b HR |
36 | |
37 | if [ -f "$TEST_DIR/qsd.pid" ]; then | |
38 | kill -SIGKILL "$(cat "$TEST_DIR/qsd.pid")" | |
39 | rm -f "$TEST_DIR/qsd.pid" | |
40 | fi | |
41 | rm -f "$SOCK_DIR/qsd.sock" | |
24575bfa KW |
42 | } |
43 | trap "_cleanup; exit \$status" 0 1 2 3 15 | |
44 | ||
45 | # get standard environment, filters and checks | |
46 | . ./common.rc | |
47 | . ./common.filter | |
48 | . ./common.qemu | |
49 | ||
50 | _supported_fmt qcow2 | |
51 | _supported_proto file | |
52 | _supported_os Linux | |
53 | ||
ad6fe44b | 54 | size=$((64 * 1048576)) |
24575bfa KW |
55 | TEST_IMG="${TEST_IMG}.base" _make_test_img $size |
56 | ||
57 | echo | |
58 | echo === Starting VM === | |
59 | echo | |
60 | ||
61 | qemu_comm_method="qmp" | |
62 | ||
63 | _launch_qemu \ | |
8dff69b9 | 64 | -drive file="${TEST_IMG}.base",cache=$CACHEMODE,aio=$AIOMODE,driver=$IMGFMT,id=disk |
24575bfa KW |
65 | h=$QEMU_HANDLE |
66 | _send_qemu_cmd $h "{ 'execute': 'qmp_capabilities' }" 'return' | |
67 | ||
68 | echo | |
69 | echo === Creating backing chain === | |
70 | echo | |
71 | ||
72 | _send_qemu_cmd $h \ | |
73 | "{ 'execute': 'blockdev-snapshot-sync', | |
74 | 'arguments': { 'device': 'disk', | |
75 | 'snapshot-file': '$TEST_IMG.mid', | |
76 | 'format': '$IMGFMT', | |
77 | 'mode': 'absolute-paths' } }" \ | |
78 | "return" | |
79 | ||
80 | _send_qemu_cmd $h \ | |
81 | "{ 'execute': 'human-monitor-command', | |
82 | 'arguments': { 'command-line': | |
83 | 'qemu-io disk \"write 0 4M\"' } }" \ | |
84 | "return" | |
85 | ||
86 | _send_qemu_cmd $h \ | |
87 | "{ 'execute': 'blockdev-snapshot-sync', | |
88 | 'arguments': { 'device': 'disk', | |
89 | 'snapshot-file': '$TEST_IMG', | |
90 | 'format': '$IMGFMT', | |
91 | 'mode': 'absolute-paths' } }" \ | |
92 | "return" | |
93 | ||
94 | echo | |
95 | echo === Start commit job and exit qemu === | |
96 | echo | |
97 | ||
98 | # Note that the reference output intentionally includes the 'offset' field in | |
c1de5696 SH |
99 | # BLOCK_JOB_* events for all of the following block jobs. They are predictable |
100 | # and any change in the offsets would hint at a bug in the job throttling code. | |
24575bfa KW |
101 | # |
102 | # In order to achieve these predictable offsets, all of the following tests | |
103 | # use speed=65536. Each job will perform exactly one iteration before it has | |
104 | # to sleep at least for a second, which is plenty of time for the 'quit' QMP | |
105 | # command to be received (after receiving the command, the rest runs | |
106 | # synchronously, so jobs can arbitrarily continue or complete). | |
107 | # | |
108 | # The buffer size for commit and streaming is 512k (waiting for 8 seconds after | |
109 | # the first request), for active commit and mirror it's large enough to cover | |
110 | # the full 4M, and for backup it's the qcow2 cluster size, which we know is | |
111 | # 64k. As all of these are at least as large as the speed, we are sure that the | |
4c7e813c | 112 | # offset advances exactly once before qemu exits. |
24575bfa KW |
113 | |
114 | _send_qemu_cmd $h \ | |
115 | "{ 'execute': 'block-commit', | |
116 | 'arguments': { 'device': 'disk', | |
117 | 'base':'$TEST_IMG.base', | |
118 | 'top': '$TEST_IMG.mid', | |
119 | 'speed': 65536 } }" \ | |
120 | "return" | |
121 | ||
ddf2d98a SH |
122 | # If we don't sleep here 'quit' command races with disk I/O |
123 | sleep 0.5 | |
124 | ||
1dac83f1 KW |
125 | # Ignore the JOB_STATUS_CHANGE events while shutting down the VM. Depending on |
126 | # the timing, jobs may or may not transition through a paused state. | |
24575bfa | 127 | _send_qemu_cmd $h "{ 'execute': 'quit' }" "return" |
1dac83f1 | 128 | wait=1 _cleanup_qemu | grep -v 'JOB_STATUS_CHANGE' |
24575bfa KW |
129 | |
130 | echo | |
131 | echo === Start active commit job and exit qemu === | |
132 | echo | |
133 | ||
134 | _launch_qemu \ | |
8dff69b9 | 135 | -drive file="${TEST_IMG}",cache=$CACHEMODE,aio=$AIOMODE,driver=$IMGFMT,id=disk |
24575bfa KW |
136 | h=$QEMU_HANDLE |
137 | _send_qemu_cmd $h "{ 'execute': 'qmp_capabilities' }" 'return' | |
138 | ||
139 | _send_qemu_cmd $h \ | |
140 | "{ 'execute': 'block-commit', | |
141 | 'arguments': { 'device': 'disk', | |
142 | 'base':'$TEST_IMG.base', | |
143 | 'speed': 65536 } }" \ | |
144 | "return" | |
145 | ||
ddf2d98a SH |
146 | # If we don't sleep here 'quit' command races with disk I/O |
147 | sleep 0.5 | |
148 | ||
24575bfa | 149 | _send_qemu_cmd $h "{ 'execute': 'quit' }" "return" |
1dac83f1 | 150 | wait=1 _cleanup_qemu | grep -v 'JOB_STATUS_CHANGE' |
24575bfa KW |
151 | |
152 | echo | |
153 | echo === Start mirror job and exit qemu === | |
154 | echo | |
155 | ||
156 | _launch_qemu \ | |
8dff69b9 | 157 | -drive file="${TEST_IMG}",cache=$CACHEMODE,aio=$AIOMODE,driver=$IMGFMT,id=disk |
24575bfa KW |
158 | h=$QEMU_HANDLE |
159 | _send_qemu_cmd $h "{ 'execute': 'qmp_capabilities' }" 'return' | |
160 | ||
161 | _send_qemu_cmd $h \ | |
162 | "{ 'execute': 'drive-mirror', | |
163 | 'arguments': { 'device': 'disk', | |
164 | 'target': '$TEST_IMG.copy', | |
165 | 'format': '$IMGFMT', | |
166 | 'sync': 'full', | |
167 | 'speed': 65536 } }" \ | |
168 | "return" | |
169 | ||
8565c3ab VSO |
170 | # If we don't sleep here 'quit' command may be handled before |
171 | # the first mirror iteration is done | |
172 | sleep 0.5 | |
173 | ||
24575bfa | 174 | _send_qemu_cmd $h "{ 'execute': 'quit' }" "return" |
1dac83f1 | 175 | wait=1 _cleanup_qemu | grep -v 'JOB_STATUS_CHANGE' |
24575bfa KW |
176 | |
177 | echo | |
178 | echo === Start backup job and exit qemu === | |
179 | echo | |
180 | ||
181 | _launch_qemu \ | |
8dff69b9 | 182 | -drive file="${TEST_IMG}",cache=$CACHEMODE,aio=$AIOMODE,driver=$IMGFMT,id=disk |
24575bfa KW |
183 | h=$QEMU_HANDLE |
184 | _send_qemu_cmd $h "{ 'execute': 'qmp_capabilities' }" 'return' | |
185 | ||
186 | _send_qemu_cmd $h \ | |
187 | "{ 'execute': 'drive-backup', | |
188 | 'arguments': { 'device': 'disk', | |
189 | 'target': '$TEST_IMG.copy', | |
190 | 'format': '$IMGFMT', | |
191 | 'sync': 'full', | |
61623f82 VSO |
192 | 'speed': 65536, |
193 | 'x-perf': {'max-chunk': 65536} } }" \ | |
24575bfa KW |
194 | "return" |
195 | ||
ddf2d98a SH |
196 | # If we don't sleep here 'quit' command races with disk I/O |
197 | sleep 0.5 | |
198 | ||
24575bfa | 199 | _send_qemu_cmd $h "{ 'execute': 'quit' }" "return" |
1dac83f1 | 200 | wait=1 _cleanup_qemu | grep -v 'JOB_STATUS_CHANGE' |
24575bfa KW |
201 | |
202 | echo | |
203 | echo === Start streaming job and exit qemu === | |
204 | echo | |
205 | ||
206 | _launch_qemu \ | |
8dff69b9 | 207 | -drive file="${TEST_IMG}",cache=$CACHEMODE,aio=$AIOMODE,driver=$IMGFMT,id=disk |
24575bfa KW |
208 | h=$QEMU_HANDLE |
209 | _send_qemu_cmd $h "{ 'execute': 'qmp_capabilities' }" 'return' | |
210 | ||
211 | _send_qemu_cmd $h \ | |
212 | "{ 'execute': 'block-stream', | |
213 | 'arguments': { 'device': 'disk', | |
214 | 'speed': 65536 } }" \ | |
215 | "return" | |
216 | ||
ddf2d98a SH |
217 | # If we don't sleep here 'quit' command races with disk I/O |
218 | sleep 0.5 | |
219 | ||
24575bfa | 220 | _send_qemu_cmd $h "{ 'execute': 'quit' }" "return" |
1dac83f1 | 221 | wait=1 _cleanup_qemu | grep -v 'JOB_STATUS_CHANGE' |
24575bfa KW |
222 | |
223 | _check_test_img | |
224 | ||
ad6fe44b HR |
225 | echo |
226 | echo === Start mirror to throttled QSD and exit qemu === | |
227 | echo | |
228 | ||
229 | # Mirror to a throttled QSD instance (so that qemu cannot drain the | |
230 | # throttling), wait for READY, then write some data to the device, | |
231 | # and then quit qemu. | |
232 | # (qemu should force-cancel the job and not wait for the data to be | |
233 | # written to the target.) | |
234 | ||
235 | _make_test_img $size | |
236 | ||
237 | # Will be used by this and the next case | |
238 | set_up_throttled_qsd() { | |
239 | $QSD \ | |
240 | --object throttle-group,id=thrgr,limits.bps-total=1048576 \ | |
241 | --blockdev null-co,node-name=null,size=$size \ | |
242 | --blockdev throttle,node-name=throttled,throttle-group=thrgr,file=null \ | |
243 | --nbd-server addr.type=unix,addr.path="$SOCK_DIR/qsd.sock" \ | |
244 | --export nbd,id=exp,node-name=throttled,name=target,writable=true \ | |
245 | --pidfile "$TEST_DIR/qsd.pid" \ | |
246 | --daemonize | |
247 | } | |
248 | ||
249 | set_up_throttled_qsd | |
250 | ||
251 | # Need a virtio-blk device so that qemu-io writes will not block the monitor | |
252 | _launch_qemu \ | |
253 | --blockdev file,node-name=source-proto,filename="$TEST_IMG" \ | |
254 | --blockdev qcow2,node-name=source-fmt,file=source-proto \ | |
255 | --device virtio-blk,id=vblk,drive=source-fmt \ | |
256 | --blockdev "{\"driver\": \"nbd\", | |
257 | \"node-name\": \"target\", | |
258 | \"server\": { | |
259 | \"type\": \"unix\", | |
260 | \"path\": \"$SOCK_DIR/qsd.sock\" | |
261 | }, | |
262 | \"export\": \"target\"}" | |
263 | ||
264 | h=$QEMU_HANDLE | |
265 | _send_qemu_cmd $h '{"execute": "qmp_capabilities"}' 'return' | |
266 | ||
267 | # Use sync=top, so the first pass will not copy the whole image | |
268 | _send_qemu_cmd $h \ | |
269 | '{"execute": "blockdev-mirror", | |
270 | "arguments": { | |
271 | "job-id": "mirror", | |
272 | "device": "source-fmt", | |
273 | "target": "target", | |
274 | "sync": "top" | |
275 | }}' \ | |
276 | 'return' \ | |
277 | | grep -v JOB_STATUS_CHANGE # Ignore these events during creation | |
278 | ||
279 | # This too will be used by this and the next case | |
280 | # $1: QEMU handle | |
281 | # $2: Image size | |
282 | wait_for_job_and_quit() { | |
283 | h=$1 | |
284 | size=$2 | |
285 | ||
286 | # List of expected events | |
287 | capture_events='BLOCK_JOB_READY JOB_STATUS_CHANGE' | |
288 | _wait_event $h 'BLOCK_JOB_READY' | |
289 | QEMU_EVENTS= # Ignore all JOB_STATUS_CHANGE events that came before READY | |
290 | ||
291 | # Write something to the device for post-READY mirroring. Write it in | |
292 | # blocks matching the cluster size, each spaced one block apart, so | |
293 | # that the mirror job will have to spawn one request per cluster. | |
294 | # Because the number of concurrent requests is limited (to 16), this | |
295 | # limits the number of bytes concurrently in flight, which speeds up | |
296 | # cancelling the job (in-flight requests still are waited for). | |
297 | # To limit the number of bytes in flight, we could alternatively pass | |
298 | # something for blockdev-mirror's @buf-size parameter, but | |
299 | # block-commit does not have such a parameter, so we need to figure | |
300 | # something out that works for both. | |
301 | ||
302 | cluster_size=65536 | |
303 | step=$((cluster_size * 2)) | |
304 | ||
305 | echo '--- Writing data to the virtio-blk device ---' | |
306 | ||
307 | for ofs in $(seq 0 $step $((size - step))); do | |
308 | qemu_io_cmd="qemu-io -d vblk/virtio-backend " | |
309 | qemu_io_cmd+="\\\"aio_write $ofs $cluster_size\\\"" | |
310 | ||
311 | # Do not include these requests in the reference output | |
312 | # (it's just too much) | |
313 | silent=yes _send_qemu_cmd $h \ | |
314 | "{\"execute\": \"human-monitor-command\", | |
315 | \"arguments\": { | |
316 | \"command-line\": \"$qemu_io_cmd\" | |
317 | }}" \ | |
318 | 'return' | |
319 | done | |
320 | ||
321 | # Wait until the job's length is updated to reflect the write requests | |
322 | ||
323 | # We have written to half of the device, so this is the expected job length | |
324 | final_len=$((size / 2)) | |
325 | timeout=100 # unit: 0.1 seconds | |
326 | while true; do | |
327 | len=$( | |
328 | _send_qemu_cmd $h \ | |
329 | '{"execute": "query-block-jobs"}' \ | |
330 | 'return.*"len": [0-9]\+' \ | |
331 | | grep 'return.*"len": [0-9]\+' \ | |
332 | | sed -e 's/.*"len": \([0-9]\+\).*/\1/' | |
333 | ) | |
334 | if [ "$len" -eq "$final_len" ]; then | |
335 | break | |
336 | fi | |
337 | timeout=$((timeout - 1)) | |
338 | if [ "$timeout" -eq 0 ]; then | |
339 | echo "ERROR: Timeout waiting for job to reach len=$final_len" | |
340 | break | |
341 | fi | |
342 | sleep 0.1 | |
343 | done | |
344 | ||
345 | sleep 1 | |
346 | ||
effd60c8 SH |
347 | # List of expected events |
348 | capture_events='BLOCK_JOB_CANCELLED JOB_STATUS_CHANGE SHUTDOWN' | |
349 | ||
ad6fe44b HR |
350 | _send_qemu_cmd $h \ |
351 | '{"execute": "quit"}' \ | |
352 | 'return' | |
353 | ||
ad6fe44b | 354 | _wait_event $h 'SHUTDOWN' |
ad6fe44b HR |
355 | _wait_event $h 'JOB_STATUS_CHANGE' # standby |
356 | _wait_event $h 'JOB_STATUS_CHANGE' # ready | |
65c23ef1 FE |
357 | _wait_event $h 'JOB_STATUS_CHANGE' # standby |
358 | _wait_event $h 'JOB_STATUS_CHANGE' # ready | |
ad6fe44b HR |
359 | _wait_event $h 'JOB_STATUS_CHANGE' # aborting |
360 | # Filter the offset (depends on when exactly `quit` was issued) | |
361 | _wait_event $h 'BLOCK_JOB_CANCELLED' \ | |
362 | | sed -e 's/"offset": [0-9]\+/"offset": (filtered)/' | |
363 | _wait_event $h 'JOB_STATUS_CHANGE' # concluded | |
364 | _wait_event $h 'JOB_STATUS_CHANGE' # null | |
365 | ||
366 | wait=yes _cleanup_qemu | |
367 | ||
368 | kill -SIGTERM "$(cat "$TEST_DIR/qsd.pid")" | |
369 | } | |
370 | ||
371 | wait_for_job_and_quit $h $size | |
372 | ||
373 | echo | |
374 | echo === Start active commit to throttled QSD and exit qemu === | |
375 | echo | |
376 | ||
377 | # Same as the above, but instead of mirroring, do an active commit | |
378 | ||
379 | _make_test_img $size | |
380 | ||
381 | set_up_throttled_qsd | |
382 | ||
383 | _launch_qemu \ | |
384 | --blockdev "{\"driver\": \"nbd\", | |
385 | \"node-name\": \"target\", | |
386 | \"server\": { | |
387 | \"type\": \"unix\", | |
388 | \"path\": \"$SOCK_DIR/qsd.sock\" | |
389 | }, | |
390 | \"export\": \"target\"}" \ | |
391 | --blockdev file,node-name=source-proto,filename="$TEST_IMG" \ | |
392 | --blockdev qcow2,node-name=source-fmt,file=source-proto,backing=target \ | |
393 | --device virtio-blk,id=vblk,drive=source-fmt | |
394 | ||
395 | h=$QEMU_HANDLE | |
396 | _send_qemu_cmd $h '{"execute": "qmp_capabilities"}' 'return' | |
397 | ||
398 | _send_qemu_cmd $h \ | |
399 | '{"execute": "block-commit", | |
400 | "arguments": { | |
401 | "job-id": "commit", | |
402 | "device": "source-fmt" | |
403 | }}' \ | |
404 | 'return' \ | |
405 | | grep -v JOB_STATUS_CHANGE # Ignore these events during creation | |
406 | ||
407 | wait_for_job_and_quit $h $size | |
408 | ||
24575bfa KW |
409 | # success, all done |
410 | echo "*** done" | |
411 | rm -f $seq.full | |
412 | status=0 |