root / tests / qemu-iotests / 030 @ ecc1c88e
History | View | Annotate | Download (20 kB)
1 |
#!/usr/bin/env python |
---|---|
2 |
# |
3 |
# Tests for image streaming. |
4 |
# |
5 |
# Copyright (C) 2012 IBM Corp. |
6 |
# |
7 |
# This program is free software; you can redistribute it and/or modify |
8 |
# it under the terms of the GNU General Public License as published by |
9 |
# the Free Software Foundation; either version 2 of the License, or |
10 |
# (at your option) any later version. |
11 |
# |
12 |
# This program is distributed in the hope that it will be useful, |
13 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 |
# GNU General Public License for more details. |
16 |
# |
17 |
# You should have received a copy of the GNU General Public License |
18 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
19 |
# |
20 |
|
21 |
import time |
22 |
import os |
23 |
import iotests |
24 |
from iotests import qemu_img, qemu_io |
25 |
import struct |
26 |
|
27 |
backing_img = os.path.join(iotests.test_dir, 'backing.img') |
28 |
mid_img = os.path.join(iotests.test_dir, 'mid.img') |
29 |
test_img = os.path.join(iotests.test_dir, 'test.img') |
30 |
|
31 |
class ImageStreamingTestCase(iotests.QMPTestCase): |
32 |
'''Abstract base class for image streaming test cases''' |
33 |
|
34 |
def cancel_and_wait(self, drive='drive0'): |
35 |
'''Cancel a block job and wait for it to finish''' |
36 |
result = self.vm.qmp('block-job-cancel', device=drive) |
37 |
self.assert_qmp(result, 'return', {}) |
38 |
|
39 |
cancelled = False |
40 |
while not cancelled: |
41 |
for event in self.vm.get_qmp_events(wait=True): |
42 |
if event['event'] == 'BLOCK_JOB_CANCELLED': |
43 |
self.assert_qmp(event, 'data/type', 'stream') |
44 |
self.assert_qmp(event, 'data/device', drive) |
45 |
cancelled = True |
46 |
|
47 |
self.assert_no_active_block_jobs() |
48 |
|
49 |
def create_image(self, name, size): |
50 |
file = open(name, 'w') |
51 |
i = 0 |
52 |
while i < size: |
53 |
sector = struct.pack('>l504xl', i / 512, i / 512) |
54 |
file.write(sector) |
55 |
i = i + 512 |
56 |
file.close() |
57 |
|
58 |
|
59 |
class TestSingleDrive(ImageStreamingTestCase): |
60 |
image_len = 1 * 1024 * 1024 # MB |
61 |
|
62 |
def setUp(self): |
63 |
self.create_image(backing_img, TestSingleDrive.image_len) |
64 |
qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, mid_img) |
65 |
qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % mid_img, test_img) |
66 |
self.vm = iotests.VM().add_drive(test_img) |
67 |
self.vm.launch() |
68 |
|
69 |
def tearDown(self): |
70 |
self.vm.shutdown() |
71 |
os.remove(test_img) |
72 |
os.remove(mid_img) |
73 |
os.remove(backing_img) |
74 |
|
75 |
def test_stream(self): |
76 |
self.assert_no_active_block_jobs() |
77 |
|
78 |
result = self.vm.qmp('block-stream', device='drive0') |
79 |
self.assert_qmp(result, 'return', {}) |
80 |
|
81 |
completed = False |
82 |
while not completed: |
83 |
for event in self.vm.get_qmp_events(wait=True): |
84 |
if event['event'] == 'BLOCK_JOB_COMPLETED': |
85 |
self.assert_qmp(event, 'data/type', 'stream') |
86 |
self.assert_qmp(event, 'data/device', 'drive0') |
87 |
self.assert_qmp(event, 'data/offset', self.image_len) |
88 |
self.assert_qmp(event, 'data/len', self.image_len) |
89 |
completed = True |
90 |
|
91 |
self.assert_no_active_block_jobs() |
92 |
self.vm.shutdown() |
93 |
|
94 |
self.assertEqual(qemu_io('-c', 'map', backing_img), |
95 |
qemu_io('-c', 'map', test_img), |
96 |
'image file map does not match backing file after streaming') |
97 |
|
98 |
def test_stream_pause(self): |
99 |
self.assert_no_active_block_jobs() |
100 |
|
101 |
result = self.vm.qmp('block-stream', device='drive0') |
102 |
self.assert_qmp(result, 'return', {}) |
103 |
|
104 |
result = self.vm.qmp('block-job-pause', device='drive0') |
105 |
self.assert_qmp(result, 'return', {}) |
106 |
|
107 |
time.sleep(1) |
108 |
result = self.vm.qmp('query-block-jobs') |
109 |
offset = self.dictpath(result, 'return[0]/offset') |
110 |
|
111 |
time.sleep(1) |
112 |
result = self.vm.qmp('query-block-jobs') |
113 |
self.assert_qmp(result, 'return[0]/offset', offset) |
114 |
|
115 |
result = self.vm.qmp('block-job-resume', device='drive0') |
116 |
self.assert_qmp(result, 'return', {}) |
117 |
|
118 |
completed = False |
119 |
while not completed: |
120 |
for event in self.vm.get_qmp_events(wait=True): |
121 |
if event['event'] == 'BLOCK_JOB_COMPLETED': |
122 |
self.assert_qmp(event, 'data/type', 'stream') |
123 |
self.assert_qmp(event, 'data/device', 'drive0') |
124 |
self.assert_qmp(event, 'data/offset', self.image_len) |
125 |
self.assert_qmp(event, 'data/len', self.image_len) |
126 |
completed = True |
127 |
|
128 |
self.assert_no_active_block_jobs() |
129 |
self.vm.shutdown() |
130 |
|
131 |
self.assertEqual(qemu_io('-c', 'map', backing_img), |
132 |
qemu_io('-c', 'map', test_img), |
133 |
'image file map does not match backing file after streaming') |
134 |
|
135 |
def test_stream_partial(self): |
136 |
self.assert_no_active_block_jobs() |
137 |
|
138 |
result = self.vm.qmp('block-stream', device='drive0', base=mid_img) |
139 |
self.assert_qmp(result, 'return', {}) |
140 |
|
141 |
completed = False |
142 |
while not completed: |
143 |
for event in self.vm.get_qmp_events(wait=True): |
144 |
if event['event'] == 'BLOCK_JOB_COMPLETED': |
145 |
self.assert_qmp(event, 'data/type', 'stream') |
146 |
self.assert_qmp(event, 'data/device', 'drive0') |
147 |
self.assert_qmp(event, 'data/offset', self.image_len) |
148 |
self.assert_qmp(event, 'data/len', self.image_len) |
149 |
completed = True |
150 |
|
151 |
self.assert_no_active_block_jobs() |
152 |
self.vm.shutdown() |
153 |
|
154 |
self.assertEqual(qemu_io('-c', 'map', mid_img), |
155 |
qemu_io('-c', 'map', test_img), |
156 |
'image file map does not match backing file after streaming') |
157 |
|
158 |
def test_device_not_found(self): |
159 |
result = self.vm.qmp('block-stream', device='nonexistent') |
160 |
self.assert_qmp(result, 'error/class', 'DeviceNotFound') |
161 |
|
162 |
|
163 |
class TestSmallerBackingFile(ImageStreamingTestCase): |
164 |
backing_len = 1 * 1024 * 1024 # MB |
165 |
image_len = 2 * backing_len |
166 |
|
167 |
def setUp(self): |
168 |
self.create_image(backing_img, self.backing_len) |
169 |
qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img, str(self.image_len)) |
170 |
self.vm = iotests.VM().add_drive(test_img) |
171 |
self.vm.launch() |
172 |
|
173 |
# If this hangs, then you are missing a fix to complete streaming when the |
174 |
# end of the backing file is reached. |
175 |
def test_stream(self): |
176 |
self.assert_no_active_block_jobs() |
177 |
|
178 |
result = self.vm.qmp('block-stream', device='drive0') |
179 |
self.assert_qmp(result, 'return', {}) |
180 |
|
181 |
completed = False |
182 |
while not completed: |
183 |
for event in self.vm.get_qmp_events(wait=True): |
184 |
if event['event'] == 'BLOCK_JOB_COMPLETED': |
185 |
self.assert_qmp(event, 'data/type', 'stream') |
186 |
self.assert_qmp(event, 'data/device', 'drive0') |
187 |
self.assert_qmp(event, 'data/offset', self.image_len) |
188 |
self.assert_qmp(event, 'data/len', self.image_len) |
189 |
completed = True |
190 |
|
191 |
self.assert_no_active_block_jobs() |
192 |
self.vm.shutdown() |
193 |
|
194 |
class TestErrors(ImageStreamingTestCase): |
195 |
image_len = 2 * 1024 * 1024 # MB |
196 |
|
197 |
# this should match STREAM_BUFFER_SIZE/512 in block/stream.c |
198 |
STREAM_BUFFER_SIZE = 512 * 1024 |
199 |
|
200 |
def create_blkdebug_file(self, name, event, errno): |
201 |
file = open(name, 'w') |
202 |
file.write(''' |
203 |
[inject-error] |
204 |
state = "1" |
205 |
event = "%s" |
206 |
errno = "%d" |
207 |
immediately = "off" |
208 |
once = "on" |
209 |
sector = "%d" |
210 |
|
211 |
[set-state] |
212 |
state = "1" |
213 |
event = "%s" |
214 |
new_state = "2" |
215 |
|
216 |
[set-state] |
217 |
state = "2" |
218 |
event = "%s" |
219 |
new_state = "1" |
220 |
''' % (event, errno, self.STREAM_BUFFER_SIZE / 512, event, event)) |
221 |
file.close() |
222 |
|
223 |
class TestEIO(TestErrors): |
224 |
def setUp(self): |
225 |
self.blkdebug_file = backing_img + ".blkdebug" |
226 |
self.create_image(backing_img, TestErrors.image_len) |
227 |
self.create_blkdebug_file(self.blkdebug_file, "read_aio", 5) |
228 |
qemu_img('create', '-f', iotests.imgfmt, |
229 |
'-o', 'backing_file=blkdebug:%s:%s,backing_fmt=raw' |
230 |
% (self.blkdebug_file, backing_img), |
231 |
test_img) |
232 |
self.vm = iotests.VM().add_drive(test_img) |
233 |
self.vm.launch() |
234 |
|
235 |
def tearDown(self): |
236 |
self.vm.shutdown() |
237 |
os.remove(test_img) |
238 |
os.remove(backing_img) |
239 |
os.remove(self.blkdebug_file) |
240 |
|
241 |
def test_report(self): |
242 |
self.assert_no_active_block_jobs() |
243 |
|
244 |
result = self.vm.qmp('block-stream', device='drive0') |
245 |
self.assert_qmp(result, 'return', {}) |
246 |
|
247 |
completed = False |
248 |
error = False |
249 |
while not completed: |
250 |
for event in self.vm.get_qmp_events(wait=True): |
251 |
if event['event'] == 'BLOCK_JOB_ERROR': |
252 |
self.assert_qmp(event, 'data/device', 'drive0') |
253 |
self.assert_qmp(event, 'data/operation', 'read') |
254 |
error = True |
255 |
elif event['event'] == 'BLOCK_JOB_COMPLETED': |
256 |
self.assertTrue(error, 'job completed unexpectedly') |
257 |
self.assert_qmp(event, 'data/type', 'stream') |
258 |
self.assert_qmp(event, 'data/device', 'drive0') |
259 |
self.assert_qmp(event, 'data/error', 'Input/output error') |
260 |
self.assert_qmp(event, 'data/offset', self.STREAM_BUFFER_SIZE) |
261 |
self.assert_qmp(event, 'data/len', self.image_len) |
262 |
completed = True |
263 |
|
264 |
self.assert_no_active_block_jobs() |
265 |
self.vm.shutdown() |
266 |
|
267 |
def test_ignore(self): |
268 |
self.assert_no_active_block_jobs() |
269 |
|
270 |
result = self.vm.qmp('block-stream', device='drive0', on_error='ignore') |
271 |
self.assert_qmp(result, 'return', {}) |
272 |
|
273 |
error = False |
274 |
completed = False |
275 |
while not completed: |
276 |
for event in self.vm.get_qmp_events(wait=True): |
277 |
if event['event'] == 'BLOCK_JOB_ERROR': |
278 |
self.assert_qmp(event, 'data/device', 'drive0') |
279 |
self.assert_qmp(event, 'data/operation', 'read') |
280 |
result = self.vm.qmp('query-block-jobs') |
281 |
self.assert_qmp(result, 'return[0]/paused', False) |
282 |
error = True |
283 |
elif event['event'] == 'BLOCK_JOB_COMPLETED': |
284 |
self.assertTrue(error, 'job completed unexpectedly') |
285 |
self.assert_qmp(event, 'data/type', 'stream') |
286 |
self.assert_qmp(event, 'data/device', 'drive0') |
287 |
self.assert_qmp(event, 'data/error', 'Input/output error') |
288 |
self.assert_qmp(event, 'data/offset', self.image_len) |
289 |
self.assert_qmp(event, 'data/len', self.image_len) |
290 |
completed = True |
291 |
|
292 |
self.assert_no_active_block_jobs() |
293 |
self.vm.shutdown() |
294 |
|
295 |
def test_stop(self): |
296 |
self.assert_no_active_block_jobs() |
297 |
|
298 |
result = self.vm.qmp('block-stream', device='drive0', on_error='stop') |
299 |
self.assert_qmp(result, 'return', {}) |
300 |
|
301 |
error = False |
302 |
completed = False |
303 |
while not completed: |
304 |
for event in self.vm.get_qmp_events(wait=True): |
305 |
if event['event'] == 'BLOCK_JOB_ERROR': |
306 |
self.assert_qmp(event, 'data/device', 'drive0') |
307 |
self.assert_qmp(event, 'data/operation', 'read') |
308 |
|
309 |
result = self.vm.qmp('query-block-jobs') |
310 |
self.assert_qmp(result, 'return[0]/paused', True) |
311 |
self.assert_qmp(result, 'return[0]/offset', self.STREAM_BUFFER_SIZE) |
312 |
self.assert_qmp(result, 'return[0]/io-status', 'failed') |
313 |
|
314 |
result = self.vm.qmp('block-job-resume', device='drive0') |
315 |
self.assert_qmp(result, 'return', {}) |
316 |
|
317 |
result = self.vm.qmp('query-block-jobs') |
318 |
self.assert_qmp(result, 'return[0]/paused', False) |
319 |
self.assert_qmp(result, 'return[0]/io-status', 'ok') |
320 |
error = True |
321 |
elif event['event'] == 'BLOCK_JOB_COMPLETED': |
322 |
self.assertTrue(error, 'job completed unexpectedly') |
323 |
self.assert_qmp(event, 'data/type', 'stream') |
324 |
self.assert_qmp(event, 'data/device', 'drive0') |
325 |
self.assert_qmp_absent(event, 'data/error') |
326 |
self.assert_qmp(event, 'data/offset', self.image_len) |
327 |
self.assert_qmp(event, 'data/len', self.image_len) |
328 |
completed = True |
329 |
|
330 |
self.assert_no_active_block_jobs() |
331 |
self.vm.shutdown() |
332 |
|
333 |
def test_enospc(self): |
334 |
self.assert_no_active_block_jobs() |
335 |
|
336 |
result = self.vm.qmp('block-stream', device='drive0', on_error='enospc') |
337 |
self.assert_qmp(result, 'return', {}) |
338 |
|
339 |
completed = False |
340 |
error = False |
341 |
while not completed: |
342 |
for event in self.vm.get_qmp_events(wait=True): |
343 |
if event['event'] == 'BLOCK_JOB_ERROR': |
344 |
self.assert_qmp(event, 'data/device', 'drive0') |
345 |
self.assert_qmp(event, 'data/operation', 'read') |
346 |
error = True |
347 |
elif event['event'] == 'BLOCK_JOB_COMPLETED': |
348 |
self.assertTrue(error, 'job completed unexpectedly') |
349 |
self.assert_qmp(event, 'data/type', 'stream') |
350 |
self.assert_qmp(event, 'data/device', 'drive0') |
351 |
self.assert_qmp(event, 'data/error', 'Input/output error') |
352 |
self.assert_qmp(event, 'data/offset', self.STREAM_BUFFER_SIZE) |
353 |
self.assert_qmp(event, 'data/len', self.image_len) |
354 |
completed = True |
355 |
|
356 |
self.assert_no_active_block_jobs() |
357 |
self.vm.shutdown() |
358 |
|
359 |
class TestENOSPC(TestErrors): |
360 |
def setUp(self): |
361 |
self.blkdebug_file = backing_img + ".blkdebug" |
362 |
self.create_image(backing_img, TestErrors.image_len) |
363 |
self.create_blkdebug_file(self.blkdebug_file, "read_aio", 28) |
364 |
qemu_img('create', '-f', iotests.imgfmt, |
365 |
'-o', 'backing_file=blkdebug:%s:%s,backing_fmt=raw' |
366 |
% (self.blkdebug_file, backing_img), |
367 |
test_img) |
368 |
self.vm = iotests.VM().add_drive(test_img) |
369 |
self.vm.launch() |
370 |
|
371 |
def tearDown(self): |
372 |
self.vm.shutdown() |
373 |
os.remove(test_img) |
374 |
os.remove(backing_img) |
375 |
os.remove(self.blkdebug_file) |
376 |
|
377 |
def test_enospc(self): |
378 |
self.assert_no_active_block_jobs() |
379 |
|
380 |
result = self.vm.qmp('block-stream', device='drive0', on_error='enospc') |
381 |
self.assert_qmp(result, 'return', {}) |
382 |
|
383 |
error = False |
384 |
completed = False |
385 |
while not completed: |
386 |
for event in self.vm.get_qmp_events(wait=True): |
387 |
if event['event'] == 'BLOCK_JOB_ERROR': |
388 |
self.assert_qmp(event, 'data/device', 'drive0') |
389 |
self.assert_qmp(event, 'data/operation', 'read') |
390 |
|
391 |
result = self.vm.qmp('query-block-jobs') |
392 |
self.assert_qmp(result, 'return[0]/paused', True) |
393 |
self.assert_qmp(result, 'return[0]/offset', self.STREAM_BUFFER_SIZE) |
394 |
self.assert_qmp(result, 'return[0]/io-status', 'nospace') |
395 |
|
396 |
result = self.vm.qmp('block-job-resume', device='drive0') |
397 |
self.assert_qmp(result, 'return', {}) |
398 |
|
399 |
result = self.vm.qmp('query-block-jobs') |
400 |
self.assert_qmp(result, 'return[0]/paused', False) |
401 |
self.assert_qmp(result, 'return[0]/io-status', 'ok') |
402 |
error = True |
403 |
elif event['event'] == 'BLOCK_JOB_COMPLETED': |
404 |
self.assertTrue(error, 'job completed unexpectedly') |
405 |
self.assert_qmp(event, 'data/type', 'stream') |
406 |
self.assert_qmp(event, 'data/device', 'drive0') |
407 |
self.assert_qmp_absent(event, 'data/error') |
408 |
self.assert_qmp(event, 'data/offset', self.image_len) |
409 |
self.assert_qmp(event, 'data/len', self.image_len) |
410 |
completed = True |
411 |
|
412 |
self.assert_no_active_block_jobs() |
413 |
self.vm.shutdown() |
414 |
|
415 |
class TestStreamStop(ImageStreamingTestCase): |
416 |
image_len = 8 * 1024 * 1024 * 1024 # GB |
417 |
|
418 |
def setUp(self): |
419 |
qemu_img('create', backing_img, str(TestStreamStop.image_len)) |
420 |
qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img) |
421 |
self.vm = iotests.VM().add_drive(test_img) |
422 |
self.vm.launch() |
423 |
|
424 |
def tearDown(self): |
425 |
self.vm.shutdown() |
426 |
os.remove(test_img) |
427 |
os.remove(backing_img) |
428 |
|
429 |
def test_stream_stop(self): |
430 |
self.assert_no_active_block_jobs() |
431 |
|
432 |
result = self.vm.qmp('block-stream', device='drive0') |
433 |
self.assert_qmp(result, 'return', {}) |
434 |
|
435 |
time.sleep(0.1) |
436 |
events = self.vm.get_qmp_events(wait=False) |
437 |
self.assertEqual(events, [], 'unexpected QMP event: %s' % events) |
438 |
|
439 |
self.cancel_and_wait() |
440 |
|
441 |
class TestSetSpeed(ImageStreamingTestCase): |
442 |
image_len = 80 * 1024 * 1024 # MB |
443 |
|
444 |
def setUp(self): |
445 |
qemu_img('create', backing_img, str(TestSetSpeed.image_len)) |
446 |
qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img) |
447 |
self.vm = iotests.VM().add_drive(test_img) |
448 |
self.vm.launch() |
449 |
|
450 |
def tearDown(self): |
451 |
self.vm.shutdown() |
452 |
os.remove(test_img) |
453 |
os.remove(backing_img) |
454 |
|
455 |
# This is a short performance test which is not run by default. |
456 |
# Invoke "IMGFMT=qed ./030 TestSetSpeed.perf_test_throughput" |
457 |
def perf_test_throughput(self): |
458 |
self.assert_no_active_block_jobs() |
459 |
|
460 |
result = self.vm.qmp('block-stream', device='drive0') |
461 |
self.assert_qmp(result, 'return', {}) |
462 |
|
463 |
result = self.vm.qmp('block-job-set-speed', device='drive0', speed=8 * 1024 * 1024) |
464 |
self.assert_qmp(result, 'return', {}) |
465 |
|
466 |
completed = False |
467 |
while not completed: |
468 |
for event in self.vm.get_qmp_events(wait=True): |
469 |
if event['event'] == 'BLOCK_JOB_COMPLETED': |
470 |
self.assert_qmp(event, 'data/type', 'stream') |
471 |
self.assert_qmp(event, 'data/device', 'drive0') |
472 |
self.assert_qmp(event, 'data/offset', self.image_len) |
473 |
self.assert_qmp(event, 'data/len', self.image_len) |
474 |
completed = True |
475 |
|
476 |
self.assert_no_active_block_jobs() |
477 |
|
478 |
def test_set_speed(self): |
479 |
self.assert_no_active_block_jobs() |
480 |
|
481 |
result = self.vm.qmp('block-stream', device='drive0') |
482 |
self.assert_qmp(result, 'return', {}) |
483 |
|
484 |
# Default speed is 0 |
485 |
result = self.vm.qmp('query-block-jobs') |
486 |
self.assert_qmp(result, 'return[0]/device', 'drive0') |
487 |
self.assert_qmp(result, 'return[0]/speed', 0) |
488 |
|
489 |
result = self.vm.qmp('block-job-set-speed', device='drive0', speed=8 * 1024 * 1024) |
490 |
self.assert_qmp(result, 'return', {}) |
491 |
|
492 |
# Ensure the speed we set was accepted |
493 |
result = self.vm.qmp('query-block-jobs') |
494 |
self.assert_qmp(result, 'return[0]/device', 'drive0') |
495 |
self.assert_qmp(result, 'return[0]/speed', 8 * 1024 * 1024) |
496 |
|
497 |
self.cancel_and_wait() |
498 |
|
499 |
# Check setting speed in block-stream works |
500 |
result = self.vm.qmp('block-stream', device='drive0', speed=4 * 1024 * 1024) |
501 |
self.assert_qmp(result, 'return', {}) |
502 |
|
503 |
result = self.vm.qmp('query-block-jobs') |
504 |
self.assert_qmp(result, 'return[0]/device', 'drive0') |
505 |
self.assert_qmp(result, 'return[0]/speed', 4 * 1024 * 1024) |
506 |
|
507 |
self.cancel_and_wait() |
508 |
|
509 |
def test_set_speed_invalid(self): |
510 |
self.assert_no_active_block_jobs() |
511 |
|
512 |
result = self.vm.qmp('block-stream', device='drive0', speed=-1) |
513 |
self.assert_qmp(result, 'error/class', 'GenericError') |
514 |
|
515 |
self.assert_no_active_block_jobs() |
516 |
|
517 |
result = self.vm.qmp('block-stream', device='drive0') |
518 |
self.assert_qmp(result, 'return', {}) |
519 |
|
520 |
result = self.vm.qmp('block-job-set-speed', device='drive0', speed=-1) |
521 |
self.assert_qmp(result, 'error/class', 'GenericError') |
522 |
|
523 |
self.cancel_and_wait() |
524 |
|
525 |
if __name__ == '__main__': |
526 |
iotests.main(supported_fmts=['qcow2', 'qed']) |