cirrus: fix off-by-one in cirrus_bitblt_rop_bkwd_transp_*_16
[qemu.git] / tests / qemu-iotests / 030
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
26 backing_img = os.path.join(iotests.test_dir, 'backing.img')
27 mid_img = os.path.join(iotests.test_dir, 'mid.img')
28 test_img = os.path.join(iotests.test_dir, 'test.img')
29
30 class TestSingleDrive(iotests.QMPTestCase):
31     image_len = 1 * 1024 * 1024 # MB
32
33     def setUp(self):
34         iotests.create_image(backing_img, TestSingleDrive.image_len)
35         qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, mid_img)
36         qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % mid_img, test_img)
37         qemu_io('-f', 'raw', '-c', 'write -P 0x1 0 512', backing_img)
38         qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0x1 524288 512', mid_img)
39         self.vm = iotests.VM().add_drive("blkdebug::" + test_img, "backing.node-name=mid")
40         self.vm.launch()
41
42     def tearDown(self):
43         self.vm.shutdown()
44         os.remove(test_img)
45         os.remove(mid_img)
46         os.remove(backing_img)
47
48     def test_stream(self):
49         self.assert_no_active_block_jobs()
50
51         result = self.vm.qmp('block-stream', device='drive0')
52         self.assert_qmp(result, 'return', {})
53
54         self.wait_until_completed()
55
56         self.assert_no_active_block_jobs()
57         self.vm.shutdown()
58
59         self.assertEqual(qemu_io('-f', 'raw', '-c', 'map', backing_img),
60                          qemu_io('-f', iotests.imgfmt, '-c', 'map', test_img),
61                          'image file map does not match backing file after streaming')
62
63     def test_stream_intermediate(self):
64         self.assert_no_active_block_jobs()
65
66         self.assertNotEqual(qemu_io('-f', 'raw', '-c', 'map', backing_img),
67                             qemu_io('-f', iotests.imgfmt, '-c', 'map', mid_img),
68                             'image file map matches backing file before streaming')
69
70         result = self.vm.qmp('block-stream', device='mid', job_id='stream-mid')
71         self.assert_qmp(result, 'return', {})
72
73         self.wait_until_completed(drive='stream-mid')
74
75         self.assert_no_active_block_jobs()
76         self.vm.shutdown()
77
78         self.assertEqual(qemu_io('-f', 'raw', '-c', 'map', backing_img),
79                          qemu_io('-f', iotests.imgfmt, '-c', 'map', mid_img),
80                          'image file map does not match backing file after streaming')
81
82     def test_stream_pause(self):
83         self.assert_no_active_block_jobs()
84
85         self.vm.pause_drive('drive0')
86         result = self.vm.qmp('block-stream', device='drive0')
87         self.assert_qmp(result, 'return', {})
88
89         result = self.vm.qmp('block-job-pause', device='drive0')
90         self.assert_qmp(result, 'return', {})
91
92         time.sleep(1)
93         result = self.vm.qmp('query-block-jobs')
94         offset = self.dictpath(result, 'return[0]/offset')
95
96         time.sleep(1)
97         result = self.vm.qmp('query-block-jobs')
98         self.assert_qmp(result, 'return[0]/offset', offset)
99
100         result = self.vm.qmp('block-job-resume', device='drive0')
101         self.assert_qmp(result, 'return', {})
102
103         self.vm.resume_drive('drive0')
104         self.wait_until_completed()
105
106         self.assert_no_active_block_jobs()
107         self.vm.shutdown()
108
109         self.assertEqual(qemu_io('-f', 'raw', '-c', 'map', backing_img),
110                          qemu_io('-f', iotests.imgfmt, '-c', 'map', test_img),
111                          'image file map does not match backing file after streaming')
112
113     def test_stream_no_op(self):
114         self.assert_no_active_block_jobs()
115
116         # The image map is empty before the operation
117         empty_map = qemu_io('-f', iotests.imgfmt, '-c', 'map', test_img)
118
119         # This is a no-op: no data should ever be copied from the base image
120         result = self.vm.qmp('block-stream', device='drive0', base=mid_img)
121         self.assert_qmp(result, 'return', {})
122
123         self.wait_until_completed()
124
125         self.assert_no_active_block_jobs()
126         self.vm.shutdown()
127
128         self.assertEqual(qemu_io('-f', iotests.imgfmt, '-c', 'map', test_img),
129                          empty_map, 'image file map changed after a no-op')
130
131     def test_stream_partial(self):
132         self.assert_no_active_block_jobs()
133
134         result = self.vm.qmp('block-stream', device='drive0', base=backing_img)
135         self.assert_qmp(result, 'return', {})
136
137         self.wait_until_completed()
138
139         self.assert_no_active_block_jobs()
140         self.vm.shutdown()
141
142         self.assertEqual(qemu_io('-f', iotests.imgfmt, '-c', 'map', mid_img),
143                          qemu_io('-f', iotests.imgfmt, '-c', 'map', test_img),
144                          'image file map does not match backing file after streaming')
145
146     def test_device_not_found(self):
147         result = self.vm.qmp('block-stream', device='nonexistent')
148         self.assert_qmp(result, 'error/class', 'GenericError')
149
150
151 class TestParallelOps(iotests.QMPTestCase):
152     num_ops = 4 # Number of parallel block-stream operations
153     num_imgs = num_ops * 2 + 1
154     image_len = num_ops * 1024 * 1024
155     imgs = []
156
157     def setUp(self):
158         opts = []
159         self.imgs = []
160
161         # Initialize file names and command-line options
162         for i in range(self.num_imgs):
163             img_depth = self.num_imgs - i - 1
164             opts.append("backing." * img_depth + "node-name=node%d" % i)
165             self.imgs.append(os.path.join(iotests.test_dir, 'img-%d.img' % i))
166
167         # Create all images
168         iotests.create_image(self.imgs[0], self.image_len)
169         for i in range(1, self.num_imgs):
170             qemu_img('create', '-f', iotests.imgfmt,
171                      '-o', 'backing_file=%s' % self.imgs[i-1], self.imgs[i])
172
173         # Put data into the images we are copying data from
174         for i in range(self.num_imgs / 2):
175             img_index = i * 2 + 1
176             # Alternate between 512k and 1M.
177             # This way jobs will not finish in the same order they were created
178             num_kb = 512 + 512 * (i % 2)
179             qemu_io('-f', iotests.imgfmt,
180                     '-c', 'write -P %d %d %d' % (i, i*1024*1024, num_kb * 1024),
181                     self.imgs[img_index])
182
183         # Attach the drive to the VM
184         self.vm = iotests.VM()
185         self.vm.add_drive(self.imgs[-1], ','.join(opts))
186         self.vm.launch()
187
188     def tearDown(self):
189         self.vm.shutdown()
190         for img in self.imgs:
191             os.remove(img)
192
193     # Test that it's possible to run several block-stream operations
194     # in parallel in the same snapshot chain
195     def test_stream_parallel(self):
196         self.assert_no_active_block_jobs()
197
198         # Check that the maps don't match before the streaming operations
199         for i in range(2, self.num_imgs, 2):
200             self.assertNotEqual(qemu_io('-f', iotests.imgfmt, '-c', 'map', self.imgs[i]),
201                                 qemu_io('-f', iotests.imgfmt, '-c', 'map', self.imgs[i-1]),
202                                 'image file map matches backing file before streaming')
203
204         # Create all streaming jobs
205         pending_jobs = []
206         for i in range(2, self.num_imgs, 2):
207             node_name = 'node%d' % i
208             job_id = 'stream-%s' % node_name
209             pending_jobs.append(job_id)
210             result = self.vm.qmp('block-stream', device=node_name, job_id=job_id, base=self.imgs[i-2], speed=512*1024)
211             self.assert_qmp(result, 'return', {})
212
213         # Wait for all jobs to be finished.
214         while len(pending_jobs) > 0:
215             for event in self.vm.get_qmp_events(wait=True):
216                 if event['event'] == 'BLOCK_JOB_COMPLETED':
217                     job_id = self.dictpath(event, 'data/device')
218                     self.assertTrue(job_id in pending_jobs)
219                     self.assert_qmp_absent(event, 'data/error')
220                     pending_jobs.remove(job_id)
221
222         self.assert_no_active_block_jobs()
223         self.vm.shutdown()
224
225         # Check that all maps match now
226         for i in range(2, self.num_imgs, 2):
227             self.assertEqual(qemu_io('-f', iotests.imgfmt, '-c', 'map', self.imgs[i]),
228                              qemu_io('-f', iotests.imgfmt, '-c', 'map', self.imgs[i-1]),
229                              'image file map does not match backing file after streaming')
230
231     # Test that it's not possible to perform two block-stream
232     # operations if there are nodes involved in both.
233     def test_overlapping_1(self):
234         self.assert_no_active_block_jobs()
235
236         # Set a speed limit to make sure that this job blocks the rest
237         result = self.vm.qmp('block-stream', device='node4', job_id='stream-node4', base=self.imgs[1], speed=1024*1024)
238         self.assert_qmp(result, 'return', {})
239
240         result = self.vm.qmp('block-stream', device='node5', job_id='stream-node5', base=self.imgs[2])
241         self.assert_qmp(result, 'error/class', 'GenericError')
242
243         result = self.vm.qmp('block-stream', device='node3', job_id='stream-node3', base=self.imgs[2])
244         self.assert_qmp(result, 'error/class', 'GenericError')
245
246         result = self.vm.qmp('block-stream', device='node4', job_id='stream-node4-v2')
247         self.assert_qmp(result, 'error/class', 'GenericError')
248
249         # block-commit should also fail if it touches nodes used by the stream job
250         result = self.vm.qmp('block-commit', device='drive0', base=self.imgs[4], job_id='commit-node4')
251         self.assert_qmp(result, 'error/class', 'GenericError')
252
253         result = self.vm.qmp('block-commit', device='drive0', base=self.imgs[1], top=self.imgs[3], job_id='commit-node1')
254         self.assert_qmp(result, 'error/class', 'GenericError')
255
256         # This fails because it needs to modify the backing string in node2, which is blocked
257         result = self.vm.qmp('block-commit', device='drive0', base=self.imgs[0], top=self.imgs[1], job_id='commit-node0')
258         self.assert_qmp(result, 'error/class', 'GenericError')
259
260         self.wait_until_completed(drive='stream-node4')
261         self.assert_no_active_block_jobs()
262
263     # Similar to test_overlapping_1, but with block-commit
264     # blocking the other jobs
265     def test_overlapping_2(self):
266         self.assertLessEqual(9, self.num_imgs)
267         self.assert_no_active_block_jobs()
268
269         # Set a speed limit to make sure that this job blocks the rest
270         result = self.vm.qmp('block-commit', device='drive0', top=self.imgs[5], base=self.imgs[3], job_id='commit-node3', speed=1024*1024)
271         self.assert_qmp(result, 'return', {})
272
273         result = self.vm.qmp('block-stream', device='node3', job_id='stream-node3')
274         self.assert_qmp(result, 'error/class', 'GenericError')
275
276         result = self.vm.qmp('block-stream', device='node6', base=self.imgs[2], job_id='stream-node6')
277         self.assert_qmp(result, 'error/class', 'GenericError')
278
279         result = self.vm.qmp('block-stream', device='node4', base=self.imgs[2], job_id='stream-node4')
280         self.assert_qmp(result, 'error/class', 'GenericError')
281
282         result = self.vm.qmp('block-stream', device='node6', base=self.imgs[4], job_id='stream-node6-v2')
283         self.assert_qmp(result, 'error/class', 'GenericError')
284
285         # This fails because block-commit needs to block node6, the overlay of the 'top' image
286         result = self.vm.qmp('block-stream', device='node7', base=self.imgs[5], job_id='stream-node6-v3')
287         self.assert_qmp(result, 'error/class', 'GenericError')
288
289         # This fails because block-commit currently blocks the active layer even if it's not used
290         result = self.vm.qmp('block-stream', device='drive0', base=self.imgs[5], job_id='stream-drive0')
291         self.assert_qmp(result, 'error/class', 'GenericError')
292
293         self.wait_until_completed(drive='commit-node3')
294
295     # Similar to test_overlapping_2, but here block-commit doesn't use the 'top' parameter.
296     # Internally this uses a mirror block job, hence the separate test case.
297     def test_overlapping_3(self):
298         self.assertLessEqual(8, self.num_imgs)
299         self.assert_no_active_block_jobs()
300
301         # Set a speed limit to make sure that this job blocks the rest
302         result = self.vm.qmp('block-commit', device='drive0', base=self.imgs[3], job_id='commit-drive0', speed=1024*1024)
303         self.assert_qmp(result, 'return', {})
304
305         result = self.vm.qmp('block-stream', device='node5', base=self.imgs[3], job_id='stream-node6')
306         self.assert_qmp(result, 'error/class', 'GenericError')
307
308         event = self.vm.get_qmp_event(wait=True)
309         self.assertEqual(event['event'], 'BLOCK_JOB_READY')
310         self.assert_qmp(event, 'data/device', 'commit-drive0')
311         self.assert_qmp(event, 'data/type', 'commit')
312         self.assert_qmp_absent(event, 'data/error')
313
314         result = self.vm.qmp('block-job-complete', device='commit-drive0')
315         self.assert_qmp(result, 'return', {})
316
317         self.wait_until_completed(drive='commit-drive0')
318
319     # Test a block-stream and a block-commit job in parallel
320     def test_stream_commit(self):
321         self.assertLessEqual(8, self.num_imgs)
322         self.assert_no_active_block_jobs()
323
324         # Stream from node0 into node2
325         result = self.vm.qmp('block-stream', device='node2', job_id='node2')
326         self.assert_qmp(result, 'return', {})
327
328         # Commit from the active layer into node3
329         result = self.vm.qmp('block-commit', device='drive0', base=self.imgs[3])
330         self.assert_qmp(result, 'return', {})
331
332         # Wait for all jobs to be finished.
333         pending_jobs = ['node2', 'drive0']
334         while len(pending_jobs) > 0:
335             for event in self.vm.get_qmp_events(wait=True):
336                 if event['event'] == 'BLOCK_JOB_COMPLETED':
337                     node_name = self.dictpath(event, 'data/device')
338                     self.assertTrue(node_name in pending_jobs)
339                     self.assert_qmp_absent(event, 'data/error')
340                     pending_jobs.remove(node_name)
341                 if event['event'] == 'BLOCK_JOB_READY':
342                     self.assert_qmp(event, 'data/device', 'drive0')
343                     self.assert_qmp(event, 'data/type', 'commit')
344                     self.assert_qmp_absent(event, 'data/error')
345                     self.assertTrue('drive0' in pending_jobs)
346                     self.vm.qmp('block-job-complete', device='drive0')
347
348         self.assert_no_active_block_jobs()
349
350     # Test the base_node parameter
351     def test_stream_base_node_name(self):
352         self.assert_no_active_block_jobs()
353
354         self.assertNotEqual(qemu_io('-f', iotests.imgfmt, '-c', 'map', self.imgs[4]),
355                             qemu_io('-f', iotests.imgfmt, '-c', 'map', self.imgs[3]),
356                             'image file map matches backing file before streaming')
357
358         # Error: the base node does not exist
359         result = self.vm.qmp('block-stream', device='node4', base_node='none', job_id='stream')
360         self.assert_qmp(result, 'error/class', 'GenericError')
361
362         # Error: the base node is not a backing file of the top node
363         result = self.vm.qmp('block-stream', device='node4', base_node='node6', job_id='stream')
364         self.assert_qmp(result, 'error/class', 'GenericError')
365
366         # Error: the base node is the same as the top node
367         result = self.vm.qmp('block-stream', device='node4', base_node='node4', job_id='stream')
368         self.assert_qmp(result, 'error/class', 'GenericError')
369
370         # Error: cannot specify 'base' and 'base-node' at the same time
371         result = self.vm.qmp('block-stream', device='node4', base=self.imgs[2], base_node='node2', job_id='stream')
372         self.assert_qmp(result, 'error/class', 'GenericError')
373
374         # Success: the base node is a backing file of the top node
375         result = self.vm.qmp('block-stream', device='node4', base_node='node2', job_id='stream')
376         self.assert_qmp(result, 'return', {})
377
378         self.wait_until_completed(drive='stream')
379
380         self.assert_no_active_block_jobs()
381         self.vm.shutdown()
382
383         self.assertEqual(qemu_io('-f', iotests.imgfmt, '-c', 'map', self.imgs[4]),
384                          qemu_io('-f', iotests.imgfmt, '-c', 'map', self.imgs[3]),
385                          'image file map matches backing file after streaming')
386
387 class TestQuorum(iotests.QMPTestCase):
388     num_children = 3
389     children = []
390     backing = []
391
392     def setUp(self):
393         opts = ['driver=quorum', 'vote-threshold=2']
394
395         # Initialize file names and command-line options
396         for i in range(self.num_children):
397             child_img = os.path.join(iotests.test_dir, 'img-%d.img' % i)
398             backing_img = os.path.join(iotests.test_dir, 'backing-%d.img' % i)
399             self.children.append(child_img)
400             self.backing.append(backing_img)
401             qemu_img('create', '-f', iotests.imgfmt, backing_img, '1M')
402             qemu_io('-f', iotests.imgfmt,
403                     '-c', 'write -P 0x55 0 1024', backing_img)
404             qemu_img('create', '-f', iotests.imgfmt,
405                      '-o', 'backing_file=%s' % backing_img, child_img)
406             opts.append("children.%d.file.filename=%s" % (i, child_img))
407             opts.append("children.%d.node-name=node%d" % (i, i))
408
409         # Attach the drive to the VM
410         self.vm = iotests.VM()
411         self.vm.add_drive(path = None, opts = ','.join(opts))
412         self.vm.launch()
413
414     def tearDown(self):
415         self.vm.shutdown()
416         for img in self.children:
417             os.remove(img)
418         for img in self.backing:
419             os.remove(img)
420
421     def test_stream_quorum(self):
422         if not iotests.supports_quorum():
423             return
424
425         self.assertNotEqual(qemu_io('-f', iotests.imgfmt, '-c', 'map', self.children[0]),
426                             qemu_io('-f', iotests.imgfmt, '-c', 'map', self.backing[0]),
427                             'image file map matches backing file before streaming')
428
429         self.assert_no_active_block_jobs()
430
431         result = self.vm.qmp('block-stream', device='node0', job_id='stream-node0')
432         self.assert_qmp(result, 'return', {})
433
434         self.wait_until_completed(drive='stream-node0')
435
436         self.assert_no_active_block_jobs()
437         self.vm.shutdown()
438
439         self.assertEqual(qemu_io('-f', iotests.imgfmt, '-c', 'map', self.children[0]),
440                          qemu_io('-f', iotests.imgfmt, '-c', 'map', self.backing[0]),
441                          'image file map does not match backing file after streaming')
442
443 class TestSmallerBackingFile(iotests.QMPTestCase):
444     backing_len = 1 * 1024 * 1024 # MB
445     image_len = 2 * backing_len
446
447     def setUp(self):
448         iotests.create_image(backing_img, self.backing_len)
449         qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img, str(self.image_len))
450         self.vm = iotests.VM().add_drive(test_img)
451         self.vm.launch()
452
453     # If this hangs, then you are missing a fix to complete streaming when the
454     # end of the backing file is reached.
455     def test_stream(self):
456         self.assert_no_active_block_jobs()
457
458         result = self.vm.qmp('block-stream', device='drive0')
459         self.assert_qmp(result, 'return', {})
460
461         self.wait_until_completed()
462
463         self.assert_no_active_block_jobs()
464         self.vm.shutdown()
465
466 class TestErrors(iotests.QMPTestCase):
467     image_len = 2 * 1024 * 1024 # MB
468
469     # this should match STREAM_BUFFER_SIZE/512 in block/stream.c
470     STREAM_BUFFER_SIZE = 512 * 1024
471
472     def create_blkdebug_file(self, name, event, errno):
473         file = open(name, 'w')
474         file.write('''
475 [inject-error]
476 state = "1"
477 event = "%s"
478 errno = "%d"
479 immediately = "off"
480 once = "on"
481 sector = "%d"
482
483 [set-state]
484 state = "1"
485 event = "%s"
486 new_state = "2"
487
488 [set-state]
489 state = "2"
490 event = "%s"
491 new_state = "1"
492 ''' % (event, errno, self.STREAM_BUFFER_SIZE / 512, event, event))
493         file.close()
494
495 class TestEIO(TestErrors):
496     def setUp(self):
497         self.blkdebug_file = backing_img + ".blkdebug"
498         iotests.create_image(backing_img, TestErrors.image_len)
499         self.create_blkdebug_file(self.blkdebug_file, "read_aio", 5)
500         qemu_img('create', '-f', iotests.imgfmt,
501                  '-o', 'backing_file=blkdebug:%s:%s,backing_fmt=raw'
502                        % (self.blkdebug_file, backing_img),
503                  test_img)
504         self.vm = iotests.VM().add_drive(test_img)
505         self.vm.launch()
506
507     def tearDown(self):
508         self.vm.shutdown()
509         os.remove(test_img)
510         os.remove(backing_img)
511         os.remove(self.blkdebug_file)
512
513     def test_report(self):
514         self.assert_no_active_block_jobs()
515
516         result = self.vm.qmp('block-stream', device='drive0')
517         self.assert_qmp(result, 'return', {})
518
519         completed = False
520         error = False
521         while not completed:
522             for event in self.vm.get_qmp_events(wait=True):
523                 if event['event'] == 'BLOCK_JOB_ERROR':
524                     self.assert_qmp(event, 'data/device', 'drive0')
525                     self.assert_qmp(event, 'data/operation', 'read')
526                     error = True
527                 elif event['event'] == 'BLOCK_JOB_COMPLETED':
528                     self.assertTrue(error, 'job completed unexpectedly')
529                     self.assert_qmp(event, 'data/type', 'stream')
530                     self.assert_qmp(event, 'data/device', 'drive0')
531                     self.assert_qmp(event, 'data/error', 'Input/output error')
532                     self.assert_qmp(event, 'data/offset', self.STREAM_BUFFER_SIZE)
533                     self.assert_qmp(event, 'data/len', self.image_len)
534                     completed = True
535
536         self.assert_no_active_block_jobs()
537         self.vm.shutdown()
538
539     def test_ignore(self):
540         self.assert_no_active_block_jobs()
541
542         result = self.vm.qmp('block-stream', device='drive0', on_error='ignore')
543         self.assert_qmp(result, 'return', {})
544
545         error = False
546         completed = False
547         while not completed:
548             for event in self.vm.get_qmp_events(wait=True):
549                 if event['event'] == 'BLOCK_JOB_ERROR':
550                     error = True
551                     self.assert_qmp(event, 'data/device', 'drive0')
552                     self.assert_qmp(event, 'data/operation', 'read')
553                     result = self.vm.qmp('query-block-jobs')
554                     if result == {'return': []}:
555                         # Job finished too quickly
556                         continue
557                     self.assert_qmp(result, 'return[0]/paused', False)
558                 elif event['event'] == 'BLOCK_JOB_COMPLETED':
559                     self.assertTrue(error, 'job completed unexpectedly')
560                     self.assert_qmp(event, 'data/type', 'stream')
561                     self.assert_qmp(event, 'data/device', 'drive0')
562                     self.assert_qmp(event, 'data/error', 'Input/output error')
563                     self.assert_qmp(event, 'data/offset', self.image_len)
564                     self.assert_qmp(event, 'data/len', self.image_len)
565                     completed = True
566
567         self.assert_no_active_block_jobs()
568         self.vm.shutdown()
569
570     def test_stop(self):
571         self.assert_no_active_block_jobs()
572
573         result = self.vm.qmp('block-stream', device='drive0', on_error='stop')
574         self.assert_qmp(result, 'return', {})
575
576         error = False
577         completed = False
578         while not completed:
579             for event in self.vm.get_qmp_events(wait=True):
580                 if event['event'] == 'BLOCK_JOB_ERROR':
581                     error = True
582                     self.assert_qmp(event, 'data/device', 'drive0')
583                     self.assert_qmp(event, 'data/operation', 'read')
584
585                     result = self.vm.qmp('query-block-jobs')
586                     self.assert_qmp(result, 'return[0]/paused', True)
587                     self.assert_qmp(result, 'return[0]/offset', self.STREAM_BUFFER_SIZE)
588                     self.assert_qmp(result, 'return[0]/io-status', 'failed')
589
590                     result = self.vm.qmp('block-job-resume', device='drive0')
591                     self.assert_qmp(result, 'return', {})
592
593                     result = self.vm.qmp('query-block-jobs')
594                     if result == {'return': []}:
595                         # Race; likely already finished. Check.
596                         continue
597                     self.assert_qmp(result, 'return[0]/paused', False)
598                     self.assert_qmp(result, 'return[0]/io-status', 'ok')
599                 elif event['event'] == 'BLOCK_JOB_COMPLETED':
600                     self.assertTrue(error, 'job completed unexpectedly')
601                     self.assert_qmp(event, 'data/type', 'stream')
602                     self.assert_qmp(event, 'data/device', 'drive0')
603                     self.assert_qmp_absent(event, 'data/error')
604                     self.assert_qmp(event, 'data/offset', self.image_len)
605                     self.assert_qmp(event, 'data/len', self.image_len)
606                     completed = True
607
608         self.assert_no_active_block_jobs()
609         self.vm.shutdown()
610
611     def test_enospc(self):
612         self.assert_no_active_block_jobs()
613
614         result = self.vm.qmp('block-stream', device='drive0', on_error='enospc')
615         self.assert_qmp(result, 'return', {})
616
617         completed = False
618         error = False
619         while not completed:
620             for event in self.vm.get_qmp_events(wait=True):
621                 if event['event'] == 'BLOCK_JOB_ERROR':
622                     self.assert_qmp(event, 'data/device', 'drive0')
623                     self.assert_qmp(event, 'data/operation', 'read')
624                     error = True
625                 elif event['event'] == 'BLOCK_JOB_COMPLETED':
626                     self.assertTrue(error, 'job completed unexpectedly')
627                     self.assert_qmp(event, 'data/type', 'stream')
628                     self.assert_qmp(event, 'data/device', 'drive0')
629                     self.assert_qmp(event, 'data/error', 'Input/output error')
630                     self.assert_qmp(event, 'data/offset', self.STREAM_BUFFER_SIZE)
631                     self.assert_qmp(event, 'data/len', self.image_len)
632                     completed = True
633
634         self.assert_no_active_block_jobs()
635         self.vm.shutdown()
636
637 class TestENOSPC(TestErrors):
638     def setUp(self):
639         self.blkdebug_file = backing_img + ".blkdebug"
640         iotests.create_image(backing_img, TestErrors.image_len)
641         self.create_blkdebug_file(self.blkdebug_file, "read_aio", 28)
642         qemu_img('create', '-f', iotests.imgfmt,
643                  '-o', 'backing_file=blkdebug:%s:%s,backing_fmt=raw'
644                        % (self.blkdebug_file, backing_img),
645                  test_img)
646         self.vm = iotests.VM().add_drive(test_img)
647         self.vm.launch()
648
649     def tearDown(self):
650         self.vm.shutdown()
651         os.remove(test_img)
652         os.remove(backing_img)
653         os.remove(self.blkdebug_file)
654
655     def test_enospc(self):
656         self.assert_no_active_block_jobs()
657
658         result = self.vm.qmp('block-stream', device='drive0', on_error='enospc')
659         self.assert_qmp(result, 'return', {})
660
661         error = False
662         completed = False
663         while not completed:
664             for event in self.vm.get_qmp_events(wait=True):
665                 if event['event'] == 'BLOCK_JOB_ERROR':
666                     self.assert_qmp(event, 'data/device', 'drive0')
667                     self.assert_qmp(event, 'data/operation', 'read')
668
669                     result = self.vm.qmp('query-block-jobs')
670                     self.assert_qmp(result, 'return[0]/paused', True)
671                     self.assert_qmp(result, 'return[0]/offset', self.STREAM_BUFFER_SIZE)
672                     self.assert_qmp(result, 'return[0]/io-status', 'nospace')
673
674                     result = self.vm.qmp('block-job-resume', device='drive0')
675                     self.assert_qmp(result, 'return', {})
676
677                     result = self.vm.qmp('query-block-jobs')
678                     self.assert_qmp(result, 'return[0]/paused', False)
679                     self.assert_qmp(result, 'return[0]/io-status', 'ok')
680                     error = True
681                 elif event['event'] == 'BLOCK_JOB_COMPLETED':
682                     self.assertTrue(error, 'job completed unexpectedly')
683                     self.assert_qmp(event, 'data/type', 'stream')
684                     self.assert_qmp(event, 'data/device', 'drive0')
685                     self.assert_qmp_absent(event, 'data/error')
686                     self.assert_qmp(event, 'data/offset', self.image_len)
687                     self.assert_qmp(event, 'data/len', self.image_len)
688                     completed = True
689
690         self.assert_no_active_block_jobs()
691         self.vm.shutdown()
692
693 class TestStreamStop(iotests.QMPTestCase):
694     image_len = 8 * 1024 * 1024 * 1024 # GB
695
696     def setUp(self):
697         qemu_img('create', backing_img, str(TestStreamStop.image_len))
698         qemu_io('-f', 'raw', '-c', 'write -P 0x1 0 32M', backing_img)
699         qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img)
700         qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0x1 32M 32M', test_img)
701         self.vm = iotests.VM().add_drive("blkdebug::" + test_img)
702         self.vm.launch()
703
704     def tearDown(self):
705         self.vm.shutdown()
706         os.remove(test_img)
707         os.remove(backing_img)
708
709     def test_stream_stop(self):
710         self.assert_no_active_block_jobs()
711
712         self.vm.pause_drive('drive0')
713         result = self.vm.qmp('block-stream', device='drive0')
714         self.assert_qmp(result, 'return', {})
715
716         time.sleep(0.1)
717         events = self.vm.get_qmp_events(wait=False)
718         self.assertEqual(events, [], 'unexpected QMP event: %s' % events)
719
720         self.cancel_and_wait(resume=True)
721
722 class TestSetSpeed(iotests.QMPTestCase):
723     image_len = 80 * 1024 * 1024 # MB
724
725     def setUp(self):
726         qemu_img('create', backing_img, str(TestSetSpeed.image_len))
727         qemu_io('-f', 'raw', '-c', 'write -P 0x1 0 32M', backing_img)
728         qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img)
729         qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0x1 32M 32M', test_img)
730         self.vm = iotests.VM().add_drive('blkdebug::' + test_img)
731         self.vm.launch()
732
733     def tearDown(self):
734         self.vm.shutdown()
735         os.remove(test_img)
736         os.remove(backing_img)
737
738     # This is a short performance test which is not run by default.
739     # Invoke "IMGFMT=qed ./030 TestSetSpeed.perf_test_throughput"
740     def perf_test_throughput(self):
741         self.assert_no_active_block_jobs()
742
743         result = self.vm.qmp('block-stream', device='drive0')
744         self.assert_qmp(result, 'return', {})
745
746         result = self.vm.qmp('block-job-set-speed', device='drive0', speed=8 * 1024 * 1024)
747         self.assert_qmp(result, 'return', {})
748
749         self.wait_until_completed()
750
751         self.assert_no_active_block_jobs()
752
753     def test_set_speed(self):
754         self.assert_no_active_block_jobs()
755
756         self.vm.pause_drive('drive0')
757         result = self.vm.qmp('block-stream', device='drive0')
758         self.assert_qmp(result, 'return', {})
759
760         # Default speed is 0
761         result = self.vm.qmp('query-block-jobs')
762         self.assert_qmp(result, 'return[0]/device', 'drive0')
763         self.assert_qmp(result, 'return[0]/speed', 0)
764
765         result = self.vm.qmp('block-job-set-speed', device='drive0', speed=8 * 1024 * 1024)
766         self.assert_qmp(result, 'return', {})
767
768         # Ensure the speed we set was accepted
769         result = self.vm.qmp('query-block-jobs')
770         self.assert_qmp(result, 'return[0]/device', 'drive0')
771         self.assert_qmp(result, 'return[0]/speed', 8 * 1024 * 1024)
772
773         self.cancel_and_wait(resume=True)
774         self.vm.pause_drive('drive0')
775
776         # Check setting speed in block-stream works
777         result = self.vm.qmp('block-stream', device='drive0', speed=4 * 1024 * 1024)
778         self.assert_qmp(result, 'return', {})
779
780         result = self.vm.qmp('query-block-jobs')
781         self.assert_qmp(result, 'return[0]/device', 'drive0')
782         self.assert_qmp(result, 'return[0]/speed', 4 * 1024 * 1024)
783
784         self.cancel_and_wait(resume=True)
785
786     def test_set_speed_invalid(self):
787         self.assert_no_active_block_jobs()
788
789         result = self.vm.qmp('block-stream', device='drive0', speed=-1)
790         self.assert_qmp(result, 'error/class', 'GenericError')
791
792         self.assert_no_active_block_jobs()
793
794         result = self.vm.qmp('block-stream', device='drive0')
795         self.assert_qmp(result, 'return', {})
796
797         result = self.vm.qmp('block-job-set-speed', device='drive0', speed=-1)
798         self.assert_qmp(result, 'error/class', 'GenericError')
799
800         self.cancel_and_wait()
801
802 if __name__ == '__main__':
803     iotests.main(supported_fmts=['qcow2', 'qed'])