blob: 1cdd7e2999f686eccf3e0bcfc143b4b0e53514f9 [file] [log] [blame]
Philippe Mathieu-Daudé903cb1b2020-01-30 17:32:23 +01001#!/usr/bin/env python3
Stefan Hajnoczi37ce63e2012-02-29 13:25:22 +00002#
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
Paolo Bonzini0c817342012-09-28 17:22:52 +020021import time
Stefan Hajnoczi37ce63e2012-02-29 13:25:22 +000022import os
23import iotests
24from iotests import qemu_img, qemu_io
25
26backing_img = os.path.join(iotests.test_dir, 'backing.img')
Paolo Bonzini6e343602012-05-09 15:05:03 +020027mid_img = os.path.join(iotests.test_dir, 'mid.img')
Stefan Hajnoczi37ce63e2012-02-29 13:25:22 +000028test_img = os.path.join(iotests.test_dir, 'test.img')
29
Stefan Hajnoczi2499a092013-05-28 17:11:37 +020030class TestSingleDrive(iotests.QMPTestCase):
Stefan Hajnoczi37ce63e2012-02-29 13:25:22 +000031 image_len = 1 * 1024 * 1024 # MB
32
33 def setUp(self):
Stefan Hajnoczi2499a092013-05-28 17:11:37 +020034 iotests.create_image(backing_img, TestSingleDrive.image_len)
Paolo Bonzini6e343602012-05-09 15:05:03 +020035 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)
Kevin Wolf90c9b162014-11-20 16:27:08 +010037 qemu_io('-f', 'raw', '-c', 'write -P 0x1 0 512', backing_img)
Alberto Garcia5e302a72016-03-21 15:47:26 +020038 qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0x1 524288 512', mid_img)
Max Reitz0e4a0642019-07-03 19:28:12 +020039 self.vm = iotests.VM().add_drive("blkdebug::" + test_img,
40 "backing.node-name=mid," +
41 "backing.backing.node-name=base")
Stefan Hajnoczi37ce63e2012-02-29 13:25:22 +000042 self.vm.launch()
43
44 def tearDown(self):
45 self.vm.shutdown()
46 os.remove(test_img)
Paolo Bonzini6e343602012-05-09 15:05:03 +020047 os.remove(mid_img)
Stefan Hajnoczi37ce63e2012-02-29 13:25:22 +000048 os.remove(backing_img)
49
50 def test_stream(self):
Stefan Hajnocziecc1c882013-05-28 17:11:34 +020051 self.assert_no_active_block_jobs()
Stefan Hajnoczi37ce63e2012-02-29 13:25:22 +000052
Stefan Hajnoczidb58f9c2012-04-11 16:27:10 +010053 result = self.vm.qmp('block-stream', device='drive0')
Stefan Hajnoczi37ce63e2012-02-29 13:25:22 +000054 self.assert_qmp(result, 'return', {})
55
Fam Zheng9974ad42014-04-02 13:54:07 +080056 self.wait_until_completed()
Stefan Hajnoczi37ce63e2012-02-29 13:25:22 +000057
Stefan Hajnocziecc1c882013-05-28 17:11:34 +020058 self.assert_no_active_block_jobs()
Paolo Bonzini863a5d02012-05-08 16:51:53 +020059 self.vm.shutdown()
Stefan Hajnoczi37ce63e2012-02-29 13:25:22 +000060
Kevin Wolf90c9b162014-11-20 16:27:08 +010061 self.assertEqual(qemu_io('-f', 'raw', '-c', 'map', backing_img),
62 qemu_io('-f', iotests.imgfmt, '-c', 'map', test_img),
Paolo Bonziniefcc7a22012-05-08 16:51:58 +020063 'image file map does not match backing file after streaming')
Stefan Hajnoczi37ce63e2012-02-29 13:25:22 +000064
Alberto Garcia7b8a9e52016-10-28 10:08:13 +030065 def test_stream_intermediate(self):
66 self.assert_no_active_block_jobs()
67
Fam Zhengaca70632017-05-03 00:35:42 +080068 self.assertNotEqual(qemu_io('-f', 'raw', '-rU', '-c', 'map', backing_img),
69 qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', mid_img),
Alberto Garcia7b8a9e52016-10-28 10:08:13 +030070 'image file map matches backing file before streaming')
71
72 result = self.vm.qmp('block-stream', device='mid', job_id='stream-mid')
73 self.assert_qmp(result, 'return', {})
74
75 self.wait_until_completed(drive='stream-mid')
76
77 self.assert_no_active_block_jobs()
78 self.vm.shutdown()
79
80 self.assertEqual(qemu_io('-f', 'raw', '-c', 'map', backing_img),
81 qemu_io('-f', iotests.imgfmt, '-c', 'map', mid_img),
82 'image file map does not match backing file after streaming')
83
Paolo Bonzini0c817342012-09-28 17:22:52 +020084 def test_stream_pause(self):
Stefan Hajnocziecc1c882013-05-28 17:11:34 +020085 self.assert_no_active_block_jobs()
Paolo Bonzini0c817342012-09-28 17:22:52 +020086
Fam Zhengb59b3d52013-11-20 10:01:56 +080087 self.vm.pause_drive('drive0')
Paolo Bonzini0c817342012-09-28 17:22:52 +020088 result = self.vm.qmp('block-stream', device='drive0')
89 self.assert_qmp(result, 'return', {})
90
John Snowf03d9d22018-03-10 03:27:31 -050091 self.pause_job('drive0', wait=False)
Kevin Wolf2c93c5c2017-07-21 16:41:21 +020092 self.vm.resume_drive('drive0')
John Snowf03d9d22018-03-10 03:27:31 -050093 self.pause_wait('drive0')
Kevin Wolf2c93c5c2017-07-21 16:41:21 +020094
Paolo Bonzini0c817342012-09-28 17:22:52 +020095 result = self.vm.qmp('query-block-jobs')
96 offset = self.dictpath(result, 'return[0]/offset')
97
Kevin Wolf2c93c5c2017-07-21 16:41:21 +020098 time.sleep(0.5)
Paolo Bonzini0c817342012-09-28 17:22:52 +020099 result = self.vm.qmp('query-block-jobs')
100 self.assert_qmp(result, 'return[0]/offset', offset)
101
102 result = self.vm.qmp('block-job-resume', device='drive0')
103 self.assert_qmp(result, 'return', {})
104
Fam Zheng9974ad42014-04-02 13:54:07 +0800105 self.wait_until_completed()
Paolo Bonzini0c817342012-09-28 17:22:52 +0200106
Stefan Hajnocziecc1c882013-05-28 17:11:34 +0200107 self.assert_no_active_block_jobs()
Paolo Bonzini0c817342012-09-28 17:22:52 +0200108 self.vm.shutdown()
109
Kevin Wolf90c9b162014-11-20 16:27:08 +0100110 self.assertEqual(qemu_io('-f', 'raw', '-c', 'map', backing_img),
111 qemu_io('-f', iotests.imgfmt, '-c', 'map', test_img),
Paolo Bonzini0c817342012-09-28 17:22:52 +0200112 'image file map does not match backing file after streaming')
113
Alberto Garcia409d5492016-03-21 15:47:27 +0200114 def test_stream_no_op(self):
115 self.assert_no_active_block_jobs()
116
117 # The image map is empty before the operation
Fam Zhengaca70632017-05-03 00:35:42 +0800118 empty_map = qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', test_img)
Alberto Garcia409d5492016-03-21 15:47:27 +0200119
120 # This is a no-op: no data should ever be copied from the base image
121 result = self.vm.qmp('block-stream', device='drive0', base=mid_img)
122 self.assert_qmp(result, 'return', {})
123
124 self.wait_until_completed()
125
126 self.assert_no_active_block_jobs()
127 self.vm.shutdown()
128
129 self.assertEqual(qemu_io('-f', iotests.imgfmt, '-c', 'map', test_img),
130 empty_map, 'image file map changed after a no-op')
131
Paolo Bonzini6e343602012-05-09 15:05:03 +0200132 def test_stream_partial(self):
Stefan Hajnocziecc1c882013-05-28 17:11:34 +0200133 self.assert_no_active_block_jobs()
Paolo Bonzini6e343602012-05-09 15:05:03 +0200134
Alberto Garcia5e302a72016-03-21 15:47:26 +0200135 result = self.vm.qmp('block-stream', device='drive0', base=backing_img)
Paolo Bonzini6e343602012-05-09 15:05:03 +0200136 self.assert_qmp(result, 'return', {})
137
Fam Zheng9974ad42014-04-02 13:54:07 +0800138 self.wait_until_completed()
Paolo Bonzini6e343602012-05-09 15:05:03 +0200139
Stefan Hajnocziecc1c882013-05-28 17:11:34 +0200140 self.assert_no_active_block_jobs()
Paolo Bonzini6e343602012-05-09 15:05:03 +0200141 self.vm.shutdown()
142
Kevin Wolf90c9b162014-11-20 16:27:08 +0100143 self.assertEqual(qemu_io('-f', iotests.imgfmt, '-c', 'map', mid_img),
144 qemu_io('-f', iotests.imgfmt, '-c', 'map', test_img),
Paolo Bonzini6e343602012-05-09 15:05:03 +0200145 'image file map does not match backing file after streaming')
146
Stefan Hajnoczi37ce63e2012-02-29 13:25:22 +0000147 def test_device_not_found(self):
Stefan Hajnoczidb58f9c2012-04-11 16:27:10 +0100148 result = self.vm.qmp('block-stream', device='nonexistent')
Max Reitz3f92d542019-07-03 19:28:09 +0200149 self.assert_qmp(result, 'error/desc',
150 'Cannot find device=nonexistent nor node_name=nonexistent')
Stefan Hajnoczi37ce63e2012-02-29 13:25:22 +0000151
Kevin Wolf0bb0aea2017-05-15 14:36:23 +0200152 def test_job_id_missing(self):
153 result = self.vm.qmp('block-stream', device='mid')
Max Reitz3f92d542019-07-03 19:28:09 +0200154 self.assert_qmp(result, 'error/desc', "Invalid job ID ''")
Kevin Wolf0bb0aea2017-05-15 14:36:23 +0200155
Max Reitz0e4a0642019-07-03 19:28:12 +0200156 def test_read_only(self):
157 # Create a new file that we can attach (we need a read-only top)
158 with iotests.FilePath('ro-top.img') as ro_top_path:
159 qemu_img('create', '-f', iotests.imgfmt, ro_top_path,
160 str(self.image_len))
161
162 result = self.vm.qmp('blockdev-add',
163 node_name='ro-top',
164 driver=iotests.imgfmt,
165 read_only=True,
166 file={
167 'driver': 'file',
168 'filename': ro_top_path,
169 'read-only': True
170 },
171 backing='mid')
172 self.assert_qmp(result, 'return', {})
173
174 result = self.vm.qmp('block-stream', job_id='stream',
175 device='ro-top', base_node='base')
176 self.assert_qmp(result, 'error/desc', 'Block node is read-only')
177
178 result = self.vm.qmp('blockdev-del', node_name='ro-top')
179 self.assert_qmp(result, 'return', {})
180
Stefan Hajnoczi774a8852012-08-28 15:26:49 +0100181
Alberto Garciac1a34322016-10-28 10:08:14 +0300182class TestParallelOps(iotests.QMPTestCase):
183 num_ops = 4 # Number of parallel block-stream operations
184 num_imgs = num_ops * 2 + 1
Max Reitz7229e122019-07-03 19:28:08 +0200185 image_len = num_ops * 4 * 1024 * 1024
Alberto Garciac1a34322016-10-28 10:08:14 +0300186 imgs = []
187
188 def setUp(self):
189 opts = []
190 self.imgs = []
191
192 # Initialize file names and command-line options
193 for i in range(self.num_imgs):
194 img_depth = self.num_imgs - i - 1
195 opts.append("backing." * img_depth + "node-name=node%d" % i)
196 self.imgs.append(os.path.join(iotests.test_dir, 'img-%d.img' % i))
197
198 # Create all images
199 iotests.create_image(self.imgs[0], self.image_len)
200 for i in range(1, self.num_imgs):
201 qemu_img('create', '-f', iotests.imgfmt,
202 '-o', 'backing_file=%s' % self.imgs[i-1], self.imgs[i])
203
204 # Put data into the images we are copying data from
Alberto Garcia39eaefc2018-03-06 15:01:21 +0200205 odd_img_indexes = [x for x in reversed(range(self.num_imgs)) if x % 2 == 1]
206 for i in range(len(odd_img_indexes)):
Max Reitz7229e122019-07-03 19:28:08 +0200207 # Alternate between 2MB and 4MB.
Alberto Garciac1a34322016-10-28 10:08:14 +0300208 # This way jobs will not finish in the same order they were created
Max Reitz7229e122019-07-03 19:28:08 +0200209 num_mb = 2 + 2 * (i % 2)
Alberto Garciac1a34322016-10-28 10:08:14 +0300210 qemu_io('-f', iotests.imgfmt,
Max Reitz7229e122019-07-03 19:28:08 +0200211 '-c', 'write -P 0xFF %dM %dM' % (i * 4, num_mb),
Alberto Garcia39eaefc2018-03-06 15:01:21 +0200212 self.imgs[odd_img_indexes[i]])
Alberto Garciac1a34322016-10-28 10:08:14 +0300213
214 # Attach the drive to the VM
215 self.vm = iotests.VM()
216 self.vm.add_drive(self.imgs[-1], ','.join(opts))
217 self.vm.launch()
218
219 def tearDown(self):
220 self.vm.shutdown()
221 for img in self.imgs:
222 os.remove(img)
223
224 # Test that it's possible to run several block-stream operations
225 # in parallel in the same snapshot chain
226 def test_stream_parallel(self):
227 self.assert_no_active_block_jobs()
228
229 # Check that the maps don't match before the streaming operations
230 for i in range(2, self.num_imgs, 2):
Fam Zhengaca70632017-05-03 00:35:42 +0800231 self.assertNotEqual(qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', self.imgs[i]),
232 qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', self.imgs[i-1]),
Alberto Garciac1a34322016-10-28 10:08:14 +0300233 'image file map matches backing file before streaming')
234
235 # Create all streaming jobs
236 pending_jobs = []
237 for i in range(2, self.num_imgs, 2):
238 node_name = 'node%d' % i
239 job_id = 'stream-%s' % node_name
240 pending_jobs.append(job_id)
241 result = self.vm.qmp('block-stream', device=node_name, job_id=job_id, base=self.imgs[i-2], speed=512*1024)
242 self.assert_qmp(result, 'return', {})
243
Max Reitz7229e122019-07-03 19:28:08 +0200244 for job in pending_jobs:
245 result = self.vm.qmp('block-job-set-speed', device=job, speed=0)
246 self.assert_qmp(result, 'return', {})
247
Alberto Garciac1a34322016-10-28 10:08:14 +0300248 # Wait for all jobs to be finished.
249 while len(pending_jobs) > 0:
250 for event in self.vm.get_qmp_events(wait=True):
251 if event['event'] == 'BLOCK_JOB_COMPLETED':
252 job_id = self.dictpath(event, 'data/device')
253 self.assertTrue(job_id in pending_jobs)
254 self.assert_qmp_absent(event, 'data/error')
255 pending_jobs.remove(job_id)
256
257 self.assert_no_active_block_jobs()
258 self.vm.shutdown()
259
260 # Check that all maps match now
261 for i in range(2, self.num_imgs, 2):
262 self.assertEqual(qemu_io('-f', iotests.imgfmt, '-c', 'map', self.imgs[i]),
263 qemu_io('-f', iotests.imgfmt, '-c', 'map', self.imgs[i-1]),
264 'image file map does not match backing file after streaming')
265
Alberto Garciaeb290b72016-10-28 10:08:15 +0300266 # Test that it's not possible to perform two block-stream
267 # operations if there are nodes involved in both.
268 def test_overlapping_1(self):
269 self.assert_no_active_block_jobs()
270
271 # Set a speed limit to make sure that this job blocks the rest
272 result = self.vm.qmp('block-stream', device='node4', job_id='stream-node4', base=self.imgs[1], speed=1024*1024)
273 self.assert_qmp(result, 'return', {})
274
275 result = self.vm.qmp('block-stream', device='node5', job_id='stream-node5', base=self.imgs[2])
Max Reitz3f92d542019-07-03 19:28:09 +0200276 self.assert_qmp(result, 'error/desc',
277 "Node 'node4' is busy: block device is in use by block job: stream")
Alberto Garciaeb290b72016-10-28 10:08:15 +0300278
279 result = self.vm.qmp('block-stream', device='node3', job_id='stream-node3', base=self.imgs[2])
Max Reitz3f92d542019-07-03 19:28:09 +0200280 self.assert_qmp(result, 'error/desc',
281 "Node 'node3' is busy: block device is in use by block job: stream")
Alberto Garciaeb290b72016-10-28 10:08:15 +0300282
283 result = self.vm.qmp('block-stream', device='node4', job_id='stream-node4-v2')
Max Reitz3f92d542019-07-03 19:28:09 +0200284 self.assert_qmp(result, 'error/desc',
285 "Node 'node4' is busy: block device is in use by block job: stream")
Alberto Garciaeb290b72016-10-28 10:08:15 +0300286
287 # block-commit should also fail if it touches nodes used by the stream job
288 result = self.vm.qmp('block-commit', device='drive0', base=self.imgs[4], job_id='commit-node4')
Max Reitz3f92d542019-07-03 19:28:09 +0200289 self.assert_qmp(result, 'error/desc',
290 "Node 'node4' is busy: block device is in use by block job: stream")
Alberto Garciaeb290b72016-10-28 10:08:15 +0300291
292 result = self.vm.qmp('block-commit', device='drive0', base=self.imgs[1], top=self.imgs[3], job_id='commit-node1')
Max Reitz3f92d542019-07-03 19:28:09 +0200293 self.assert_qmp(result, 'error/desc',
294 "Node 'node3' is busy: block device is in use by block job: stream")
Alberto Garciaeb290b72016-10-28 10:08:15 +0300295
296 # This fails because it needs to modify the backing string in node2, which is blocked
297 result = self.vm.qmp('block-commit', device='drive0', base=self.imgs[0], top=self.imgs[1], job_id='commit-node0')
Max Reitz3f92d542019-07-03 19:28:09 +0200298 self.assert_qmp(result, 'error/desc',
299 "Node 'node2' is busy: block device is in use by block job: stream")
Alberto Garciaeb290b72016-10-28 10:08:15 +0300300
Max Reitz7229e122019-07-03 19:28:08 +0200301 result = self.vm.qmp('block-job-set-speed', device='stream-node4', speed=0)
302 self.assert_qmp(result, 'return', {})
303
Alberto Garciaeb290b72016-10-28 10:08:15 +0300304 self.wait_until_completed(drive='stream-node4')
305 self.assert_no_active_block_jobs()
306
307 # Similar to test_overlapping_1, but with block-commit
308 # blocking the other jobs
309 def test_overlapping_2(self):
310 self.assertLessEqual(9, self.num_imgs)
311 self.assert_no_active_block_jobs()
312
313 # Set a speed limit to make sure that this job blocks the rest
314 result = self.vm.qmp('block-commit', device='drive0', top=self.imgs[5], base=self.imgs[3], job_id='commit-node3', speed=1024*1024)
315 self.assert_qmp(result, 'return', {})
316
317 result = self.vm.qmp('block-stream', device='node3', job_id='stream-node3')
Max Reitz3f92d542019-07-03 19:28:09 +0200318 self.assert_qmp(result, 'error/desc',
319 "Node 'node3' is busy: block device is in use by block job: commit")
Alberto Garciaeb290b72016-10-28 10:08:15 +0300320
321 result = self.vm.qmp('block-stream', device='node6', base=self.imgs[2], job_id='stream-node6')
Max Reitz3f92d542019-07-03 19:28:09 +0200322 self.assert_qmp(result, 'error/desc',
323 "Node 'node5' is busy: block device is in use by block job: commit")
Alberto Garciaeb290b72016-10-28 10:08:15 +0300324
325 result = self.vm.qmp('block-stream', device='node4', base=self.imgs[2], job_id='stream-node4')
Max Reitz3f92d542019-07-03 19:28:09 +0200326 self.assert_qmp(result, 'error/desc',
327 "Node 'node4' is busy: block device is in use by block job: commit")
Alberto Garciaeb290b72016-10-28 10:08:15 +0300328
329 result = self.vm.qmp('block-stream', device='node6', base=self.imgs[4], job_id='stream-node6-v2')
Max Reitz3f92d542019-07-03 19:28:09 +0200330 self.assert_qmp(result, 'error/desc',
331 "Node 'node5' is busy: block device is in use by block job: commit")
Alberto Garciaeb290b72016-10-28 10:08:15 +0300332
Alberto Garciaeb290b72016-10-28 10:08:15 +0300333 # This fails because block-commit currently blocks the active layer even if it's not used
334 result = self.vm.qmp('block-stream', device='drive0', base=self.imgs[5], job_id='stream-drive0')
Max Reitz3f92d542019-07-03 19:28:09 +0200335 self.assert_qmp(result, 'error/desc',
336 "Node 'drive0' is busy: block device is in use by block job: commit")
Alberto Garciaeb290b72016-10-28 10:08:15 +0300337
Max Reitz7229e122019-07-03 19:28:08 +0200338 result = self.vm.qmp('block-job-set-speed', device='commit-node3', speed=0)
339 self.assert_qmp(result, 'return', {})
340
Alberto Garciaeb290b72016-10-28 10:08:15 +0300341 self.wait_until_completed(drive='commit-node3')
342
343 # Similar to test_overlapping_2, but here block-commit doesn't use the 'top' parameter.
344 # Internally this uses a mirror block job, hence the separate test case.
345 def test_overlapping_3(self):
346 self.assertLessEqual(8, self.num_imgs)
347 self.assert_no_active_block_jobs()
348
349 # Set a speed limit to make sure that this job blocks the rest
350 result = self.vm.qmp('block-commit', device='drive0', base=self.imgs[3], job_id='commit-drive0', speed=1024*1024)
351 self.assert_qmp(result, 'return', {})
352
353 result = self.vm.qmp('block-stream', device='node5', base=self.imgs[3], job_id='stream-node6')
Max Reitz3f92d542019-07-03 19:28:09 +0200354 self.assert_qmp(result, 'error/desc',
355 "Node 'node5' is busy: block device is in use by block job: commit")
Alberto Garciaeb290b72016-10-28 10:08:15 +0300356
Kevin Wolfb1b30ff2020-05-13 12:00:25 +0200357 result = self.vm.qmp('block-job-set-speed', device='commit-drive0', speed=0)
358 self.assert_qmp(result, 'return', {})
359
Kevin Wolf1dac83f2018-04-30 19:09:46 +0200360 event = self.vm.event_wait(name='BLOCK_JOB_READY')
Alberto Garciaeb290b72016-10-28 10:08:15 +0300361 self.assert_qmp(event, 'data/device', 'commit-drive0')
362 self.assert_qmp(event, 'data/type', 'commit')
363 self.assert_qmp_absent(event, 'data/error')
364
365 result = self.vm.qmp('block-job-complete', device='commit-drive0')
366 self.assert_qmp(result, 'return', {})
367
368 self.wait_until_completed(drive='commit-drive0')
Alberto Garcia704d59f2016-10-28 10:08:16 +0300369
Alberto Garciad20ba602019-03-28 18:25:11 +0200370 # In this case the base node of the stream job is the same as the
Max Reitz3f92d542019-07-03 19:28:09 +0200371 # top node of commit job. Since this results in the commit filter
372 # node being part of the stream chain, this is not allowed.
Alberto Garciad20ba602019-03-28 18:25:11 +0200373 def test_overlapping_4(self):
374 self.assert_no_active_block_jobs()
375
376 # Commit from node2 into node0
Max Reitz7229e122019-07-03 19:28:08 +0200377 result = self.vm.qmp('block-commit', device='drive0',
378 top=self.imgs[2], base=self.imgs[0],
Max Reitz3f92d542019-07-03 19:28:09 +0200379 filter_node_name='commit-filter', speed=1024*1024)
Alberto Garciad20ba602019-03-28 18:25:11 +0200380 self.assert_qmp(result, 'return', {})
381
382 # Stream from node2 into node4
383 result = self.vm.qmp('block-stream', device='node4', base_node='node2', job_id='node4')
Max Reitz3f92d542019-07-03 19:28:09 +0200384 self.assert_qmp(result, 'error/desc',
385 "Cannot freeze 'backing' link to 'commit-filter'")
Alberto Garciad20ba602019-03-28 18:25:11 +0200386
Max Reitz7229e122019-07-03 19:28:08 +0200387 result = self.vm.qmp('block-job-set-speed', device='drive0', speed=0)
388 self.assert_qmp(result, 'return', {})
389
Alberto Garciad20ba602019-03-28 18:25:11 +0200390 self.wait_until_completed()
391 self.assert_no_active_block_jobs()
392
Max Reitz13658cd2019-07-03 19:28:11 +0200393 # In this case the base node of the stream job is the commit job's
394 # filter node. stream does not have a real dependency on its base
395 # node, so even though commit removes it when it is done, there is
396 # no conflict.
397 def test_overlapping_5(self):
398 self.assert_no_active_block_jobs()
399
400 # Commit from node2 into node0
401 result = self.vm.qmp('block-commit', device='drive0',
402 top_node='node2', base_node='node0',
403 filter_node_name='commit-filter', speed=1024*1024)
404 self.assert_qmp(result, 'return', {})
405
406 # Stream from node2 into node4
407 result = self.vm.qmp('block-stream', device='node4',
408 base_node='commit-filter', job_id='node4')
409 self.assert_qmp(result, 'return', {})
410
411 result = self.vm.qmp('block-job-set-speed', device='drive0', speed=0)
412 self.assert_qmp(result, 'return', {})
413
John Snow52ea7992020-03-30 20:00:14 -0400414 self.vm.run_job(job='drive0', auto_dismiss=True)
415 self.vm.run_job(job='node4', auto_dismiss=True)
Max Reitz13658cd2019-07-03 19:28:11 +0200416 self.assert_no_active_block_jobs()
417
Alberto Garcia704d59f2016-10-28 10:08:16 +0300418 # Test a block-stream and a block-commit job in parallel
Alberto Garcia39eaefc2018-03-06 15:01:21 +0200419 # Here the stream job is supposed to finish quickly in order to reproduce
420 # the scenario that triggers the bug fixed in 3d5d319e1221 and 1a63a907507
421 def test_stream_commit_1(self):
Alberto Garcia704d59f2016-10-28 10:08:16 +0300422 self.assertLessEqual(8, self.num_imgs)
423 self.assert_no_active_block_jobs()
424
425 # Stream from node0 into node2
Alberto Garcia39eaefc2018-03-06 15:01:21 +0200426 result = self.vm.qmp('block-stream', device='node2', base_node='node0', job_id='node2')
Alberto Garcia704d59f2016-10-28 10:08:16 +0300427 self.assert_qmp(result, 'return', {})
428
429 # Commit from the active layer into node3
430 result = self.vm.qmp('block-commit', device='drive0', base=self.imgs[3])
431 self.assert_qmp(result, 'return', {})
432
433 # Wait for all jobs to be finished.
434 pending_jobs = ['node2', 'drive0']
435 while len(pending_jobs) > 0:
436 for event in self.vm.get_qmp_events(wait=True):
437 if event['event'] == 'BLOCK_JOB_COMPLETED':
438 node_name = self.dictpath(event, 'data/device')
439 self.assertTrue(node_name in pending_jobs)
440 self.assert_qmp_absent(event, 'data/error')
441 pending_jobs.remove(node_name)
442 if event['event'] == 'BLOCK_JOB_READY':
443 self.assert_qmp(event, 'data/device', 'drive0')
444 self.assert_qmp(event, 'data/type', 'commit')
445 self.assert_qmp_absent(event, 'data/error')
446 self.assertTrue('drive0' in pending_jobs)
447 self.vm.qmp('block-job-complete', device='drive0')
448
Alberto Garciaeb290b72016-10-28 10:08:15 +0300449 self.assert_no_active_block_jobs()
450
Alberto Garcia39eaefc2018-03-06 15:01:21 +0200451 # This is similar to test_stream_commit_1 but both jobs are slowed
452 # down so they can run in parallel for a little while.
453 def test_stream_commit_2(self):
454 self.assertLessEqual(8, self.num_imgs)
455 self.assert_no_active_block_jobs()
456
457 # Stream from node0 into node4
458 result = self.vm.qmp('block-stream', device='node4', base_node='node0', job_id='node4', speed=1024*1024)
459 self.assert_qmp(result, 'return', {})
460
461 # Commit from the active layer into node5
462 result = self.vm.qmp('block-commit', device='drive0', base=self.imgs[5], speed=1024*1024)
463 self.assert_qmp(result, 'return', {})
464
Max Reitz7229e122019-07-03 19:28:08 +0200465 for job in ['drive0', 'node4']:
466 result = self.vm.qmp('block-job-set-speed', device=job, speed=0)
467 self.assert_qmp(result, 'return', {})
468
Alberto Garcia39eaefc2018-03-06 15:01:21 +0200469 # Wait for all jobs to be finished.
470 pending_jobs = ['node4', 'drive0']
471 while len(pending_jobs) > 0:
472 for event in self.vm.get_qmp_events(wait=True):
473 if event['event'] == 'BLOCK_JOB_COMPLETED':
474 node_name = self.dictpath(event, 'data/device')
475 self.assertTrue(node_name in pending_jobs)
476 self.assert_qmp_absent(event, 'data/error')
477 pending_jobs.remove(node_name)
478 if event['event'] == 'BLOCK_JOB_READY':
479 self.assert_qmp(event, 'data/device', 'drive0')
480 self.assert_qmp(event, 'data/type', 'commit')
481 self.assert_qmp_absent(event, 'data/error')
482 self.assertTrue('drive0' in pending_jobs)
483 self.vm.qmp('block-job-complete', device='drive0')
484
485 self.assert_no_active_block_jobs()
486
Alberto Garcia7eb13c92016-10-28 10:08:20 +0300487 # Test the base_node parameter
488 def test_stream_base_node_name(self):
489 self.assert_no_active_block_jobs()
490
Fam Zhengaca70632017-05-03 00:35:42 +0800491 self.assertNotEqual(qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', self.imgs[4]),
492 qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', self.imgs[3]),
Alberto Garcia7eb13c92016-10-28 10:08:20 +0300493 'image file map matches backing file before streaming')
494
495 # Error: the base node does not exist
496 result = self.vm.qmp('block-stream', device='node4', base_node='none', job_id='stream')
Max Reitz3f92d542019-07-03 19:28:09 +0200497 self.assert_qmp(result, 'error/desc',
498 'Cannot find device= nor node_name=none')
Alberto Garcia7eb13c92016-10-28 10:08:20 +0300499
500 # Error: the base node is not a backing file of the top node
501 result = self.vm.qmp('block-stream', device='node4', base_node='node6', job_id='stream')
Max Reitz3f92d542019-07-03 19:28:09 +0200502 self.assert_qmp(result, 'error/desc',
503 "Node 'node6' is not a backing image of 'node4'")
Alberto Garcia7eb13c92016-10-28 10:08:20 +0300504
505 # Error: the base node is the same as the top node
506 result = self.vm.qmp('block-stream', device='node4', base_node='node4', job_id='stream')
Max Reitz3f92d542019-07-03 19:28:09 +0200507 self.assert_qmp(result, 'error/desc',
508 "Node 'node4' is not a backing image of 'node4'")
Alberto Garcia7eb13c92016-10-28 10:08:20 +0300509
510 # Error: cannot specify 'base' and 'base-node' at the same time
511 result = self.vm.qmp('block-stream', device='node4', base=self.imgs[2], base_node='node2', job_id='stream')
Max Reitz3f92d542019-07-03 19:28:09 +0200512 self.assert_qmp(result, 'error/desc',
513 "'base' and 'base-node' cannot be specified at the same time")
Alberto Garcia7eb13c92016-10-28 10:08:20 +0300514
515 # Success: the base node is a backing file of the top node
516 result = self.vm.qmp('block-stream', device='node4', base_node='node2', job_id='stream')
517 self.assert_qmp(result, 'return', {})
518
519 self.wait_until_completed(drive='stream')
520
521 self.assert_no_active_block_jobs()
522 self.vm.shutdown()
523
524 self.assertEqual(qemu_io('-f', iotests.imgfmt, '-c', 'map', self.imgs[4]),
525 qemu_io('-f', iotests.imgfmt, '-c', 'map', self.imgs[3]),
526 'image file map matches backing file after streaming')
527
Alberto Garcia48361af2016-10-28 10:08:18 +0300528class TestQuorum(iotests.QMPTestCase):
529 num_children = 3
530 children = []
531 backing = []
532
Thomas Huth9442beb2020-01-14 15:02:03 +0100533 @iotests.skip_if_unsupported(['quorum'])
Alberto Garcia48361af2016-10-28 10:08:18 +0300534 def setUp(self):
535 opts = ['driver=quorum', 'vote-threshold=2']
536
537 # Initialize file names and command-line options
538 for i in range(self.num_children):
539 child_img = os.path.join(iotests.test_dir, 'img-%d.img' % i)
540 backing_img = os.path.join(iotests.test_dir, 'backing-%d.img' % i)
541 self.children.append(child_img)
542 self.backing.append(backing_img)
543 qemu_img('create', '-f', iotests.imgfmt, backing_img, '1M')
544 qemu_io('-f', iotests.imgfmt,
545 '-c', 'write -P 0x55 0 1024', backing_img)
546 qemu_img('create', '-f', iotests.imgfmt,
547 '-o', 'backing_file=%s' % backing_img, child_img)
548 opts.append("children.%d.file.filename=%s" % (i, child_img))
549 opts.append("children.%d.node-name=node%d" % (i, i))
550
551 # Attach the drive to the VM
552 self.vm = iotests.VM()
553 self.vm.add_drive(path = None, opts = ','.join(opts))
554 self.vm.launch()
555
556 def tearDown(self):
557 self.vm.shutdown()
558 for img in self.children:
559 os.remove(img)
560 for img in self.backing:
561 os.remove(img)
562
563 def test_stream_quorum(self):
Fam Zhengaca70632017-05-03 00:35:42 +0800564 self.assertNotEqual(qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', self.children[0]),
565 qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', self.backing[0]),
Alberto Garcia48361af2016-10-28 10:08:18 +0300566 'image file map matches backing file before streaming')
567
568 self.assert_no_active_block_jobs()
569
570 result = self.vm.qmp('block-stream', device='node0', job_id='stream-node0')
571 self.assert_qmp(result, 'return', {})
572
573 self.wait_until_completed(drive='stream-node0')
574
575 self.assert_no_active_block_jobs()
576 self.vm.shutdown()
577
578 self.assertEqual(qemu_io('-f', iotests.imgfmt, '-c', 'map', self.children[0]),
579 qemu_io('-f', iotests.imgfmt, '-c', 'map', self.backing[0]),
580 'image file map does not match backing file after streaming')
581
Stefan Hajnoczi2499a092013-05-28 17:11:37 +0200582class TestSmallerBackingFile(iotests.QMPTestCase):
Stefan Hajnoczi774a8852012-08-28 15:26:49 +0100583 backing_len = 1 * 1024 * 1024 # MB
584 image_len = 2 * backing_len
585
586 def setUp(self):
Stefan Hajnoczi2499a092013-05-28 17:11:37 +0200587 iotests.create_image(backing_img, self.backing_len)
Stefan Hajnoczi774a8852012-08-28 15:26:49 +0100588 qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img, str(self.image_len))
589 self.vm = iotests.VM().add_drive(test_img)
590 self.vm.launch()
591
592 # If this hangs, then you are missing a fix to complete streaming when the
593 # end of the backing file is reached.
594 def test_stream(self):
Stefan Hajnocziecc1c882013-05-28 17:11:34 +0200595 self.assert_no_active_block_jobs()
Stefan Hajnoczi774a8852012-08-28 15:26:49 +0100596
597 result = self.vm.qmp('block-stream', device='drive0')
598 self.assert_qmp(result, 'return', {})
599
Fam Zheng9974ad42014-04-02 13:54:07 +0800600 self.wait_until_completed()
Stefan Hajnoczi774a8852012-08-28 15:26:49 +0100601
Stefan Hajnocziecc1c882013-05-28 17:11:34 +0200602 self.assert_no_active_block_jobs()
Stefan Hajnoczi774a8852012-08-28 15:26:49 +0100603 self.vm.shutdown()
604
Stefan Hajnoczi2499a092013-05-28 17:11:37 +0200605class TestErrors(iotests.QMPTestCase):
Paolo Bonzini90f0b712012-09-28 17:23:02 +0200606 image_len = 2 * 1024 * 1024 # MB
607
608 # this should match STREAM_BUFFER_SIZE/512 in block/stream.c
609 STREAM_BUFFER_SIZE = 512 * 1024
610
611 def create_blkdebug_file(self, name, event, errno):
612 file = open(name, 'w')
613 file.write('''
614[inject-error]
615state = "1"
616event = "%s"
617errno = "%d"
618immediately = "off"
619once = "on"
620sector = "%d"
621
622[set-state]
623state = "1"
624event = "%s"
625new_state = "2"
626
627[set-state]
628state = "2"
629event = "%s"
630new_state = "1"
Max Reitz9a3a9a62018-10-22 14:53:02 +0100631''' % (event, errno, self.STREAM_BUFFER_SIZE // 512, event, event))
Paolo Bonzini90f0b712012-09-28 17:23:02 +0200632 file.close()
633
634class TestEIO(TestErrors):
635 def setUp(self):
636 self.blkdebug_file = backing_img + ".blkdebug"
Stefan Hajnoczi2499a092013-05-28 17:11:37 +0200637 iotests.create_image(backing_img, TestErrors.image_len)
Paolo Bonzini90f0b712012-09-28 17:23:02 +0200638 self.create_blkdebug_file(self.blkdebug_file, "read_aio", 5)
639 qemu_img('create', '-f', iotests.imgfmt,
640 '-o', 'backing_file=blkdebug:%s:%s,backing_fmt=raw'
641 % (self.blkdebug_file, backing_img),
642 test_img)
643 self.vm = iotests.VM().add_drive(test_img)
644 self.vm.launch()
645
646 def tearDown(self):
647 self.vm.shutdown()
648 os.remove(test_img)
649 os.remove(backing_img)
650 os.remove(self.blkdebug_file)
651
652 def test_report(self):
Stefan Hajnocziecc1c882013-05-28 17:11:34 +0200653 self.assert_no_active_block_jobs()
Paolo Bonzini90f0b712012-09-28 17:23:02 +0200654
655 result = self.vm.qmp('block-stream', device='drive0')
656 self.assert_qmp(result, 'return', {})
657
658 completed = False
659 error = False
660 while not completed:
661 for event in self.vm.get_qmp_events(wait=True):
662 if event['event'] == 'BLOCK_JOB_ERROR':
663 self.assert_qmp(event, 'data/device', 'drive0')
664 self.assert_qmp(event, 'data/operation', 'read')
665 error = True
666 elif event['event'] == 'BLOCK_JOB_COMPLETED':
667 self.assertTrue(error, 'job completed unexpectedly')
668 self.assert_qmp(event, 'data/type', 'stream')
669 self.assert_qmp(event, 'data/device', 'drive0')
670 self.assert_qmp(event, 'data/error', 'Input/output error')
671 self.assert_qmp(event, 'data/offset', self.STREAM_BUFFER_SIZE)
672 self.assert_qmp(event, 'data/len', self.image_len)
673 completed = True
Kevin Wolf1dac83f2018-04-30 19:09:46 +0200674 elif event['event'] == 'JOB_STATUS_CHANGE':
675 self.assert_qmp(event, 'data/id', 'drive0')
Paolo Bonzini90f0b712012-09-28 17:23:02 +0200676
Stefan Hajnocziecc1c882013-05-28 17:11:34 +0200677 self.assert_no_active_block_jobs()
Paolo Bonzini90f0b712012-09-28 17:23:02 +0200678 self.vm.shutdown()
679
680 def test_ignore(self):
Stefan Hajnocziecc1c882013-05-28 17:11:34 +0200681 self.assert_no_active_block_jobs()
Paolo Bonzini90f0b712012-09-28 17:23:02 +0200682
683 result = self.vm.qmp('block-stream', device='drive0', on_error='ignore')
684 self.assert_qmp(result, 'return', {})
685
686 error = False
687 completed = False
688 while not completed:
689 for event in self.vm.get_qmp_events(wait=True):
690 if event['event'] == 'BLOCK_JOB_ERROR':
John Snow2c3b44d2017-02-16 17:00:00 -0500691 error = True
Paolo Bonzini90f0b712012-09-28 17:23:02 +0200692 self.assert_qmp(event, 'data/device', 'drive0')
693 self.assert_qmp(event, 'data/operation', 'read')
694 result = self.vm.qmp('query-block-jobs')
John Snow2c3b44d2017-02-16 17:00:00 -0500695 if result == {'return': []}:
696 # Job finished too quickly
697 continue
Paolo Bonzini90f0b712012-09-28 17:23:02 +0200698 self.assert_qmp(result, 'return[0]/paused', False)
Paolo Bonzini90f0b712012-09-28 17:23:02 +0200699 elif event['event'] == 'BLOCK_JOB_COMPLETED':
700 self.assertTrue(error, 'job completed unexpectedly')
701 self.assert_qmp(event, 'data/type', 'stream')
702 self.assert_qmp(event, 'data/device', 'drive0')
703 self.assert_qmp(event, 'data/error', 'Input/output error')
704 self.assert_qmp(event, 'data/offset', self.image_len)
705 self.assert_qmp(event, 'data/len', self.image_len)
706 completed = True
Kevin Wolf1dac83f2018-04-30 19:09:46 +0200707 elif event['event'] == 'JOB_STATUS_CHANGE':
708 self.assert_qmp(event, 'data/id', 'drive0')
Paolo Bonzini90f0b712012-09-28 17:23:02 +0200709
Stefan Hajnocziecc1c882013-05-28 17:11:34 +0200710 self.assert_no_active_block_jobs()
Paolo Bonzini90f0b712012-09-28 17:23:02 +0200711 self.vm.shutdown()
712
713 def test_stop(self):
Stefan Hajnocziecc1c882013-05-28 17:11:34 +0200714 self.assert_no_active_block_jobs()
Paolo Bonzini90f0b712012-09-28 17:23:02 +0200715
716 result = self.vm.qmp('block-stream', device='drive0', on_error='stop')
717 self.assert_qmp(result, 'return', {})
718
719 error = False
720 completed = False
721 while not completed:
722 for event in self.vm.get_qmp_events(wait=True):
723 if event['event'] == 'BLOCK_JOB_ERROR':
John Snow01809192015-11-11 15:27:36 -0500724 error = True
Paolo Bonzini90f0b712012-09-28 17:23:02 +0200725 self.assert_qmp(event, 'data/device', 'drive0')
726 self.assert_qmp(event, 'data/operation', 'read')
727
728 result = self.vm.qmp('query-block-jobs')
729 self.assert_qmp(result, 'return[0]/paused', True)
730 self.assert_qmp(result, 'return[0]/offset', self.STREAM_BUFFER_SIZE)
731 self.assert_qmp(result, 'return[0]/io-status', 'failed')
732
733 result = self.vm.qmp('block-job-resume', device='drive0')
734 self.assert_qmp(result, 'return', {})
735
736 result = self.vm.qmp('query-block-jobs')
John Snow01809192015-11-11 15:27:36 -0500737 if result == {'return': []}:
738 # Race; likely already finished. Check.
739 continue
Paolo Bonzini90f0b712012-09-28 17:23:02 +0200740 self.assert_qmp(result, 'return[0]/paused', False)
741 self.assert_qmp(result, 'return[0]/io-status', 'ok')
Paolo Bonzini90f0b712012-09-28 17:23:02 +0200742 elif event['event'] == 'BLOCK_JOB_COMPLETED':
743 self.assertTrue(error, 'job completed unexpectedly')
744 self.assert_qmp(event, 'data/type', 'stream')
745 self.assert_qmp(event, 'data/device', 'drive0')
746 self.assert_qmp_absent(event, 'data/error')
747 self.assert_qmp(event, 'data/offset', self.image_len)
748 self.assert_qmp(event, 'data/len', self.image_len)
749 completed = True
Kevin Wolf1dac83f2018-04-30 19:09:46 +0200750 elif event['event'] == 'JOB_STATUS_CHANGE':
751 self.assert_qmp(event, 'data/id', 'drive0')
Paolo Bonzini90f0b712012-09-28 17:23:02 +0200752
Stefan Hajnocziecc1c882013-05-28 17:11:34 +0200753 self.assert_no_active_block_jobs()
Paolo Bonzini90f0b712012-09-28 17:23:02 +0200754 self.vm.shutdown()
755
756 def test_enospc(self):
Stefan Hajnocziecc1c882013-05-28 17:11:34 +0200757 self.assert_no_active_block_jobs()
Paolo Bonzini90f0b712012-09-28 17:23:02 +0200758
759 result = self.vm.qmp('block-stream', device='drive0', on_error='enospc')
760 self.assert_qmp(result, 'return', {})
761
762 completed = False
763 error = False
764 while not completed:
765 for event in self.vm.get_qmp_events(wait=True):
766 if event['event'] == 'BLOCK_JOB_ERROR':
767 self.assert_qmp(event, 'data/device', 'drive0')
768 self.assert_qmp(event, 'data/operation', 'read')
769 error = True
770 elif event['event'] == 'BLOCK_JOB_COMPLETED':
771 self.assertTrue(error, 'job completed unexpectedly')
772 self.assert_qmp(event, 'data/type', 'stream')
773 self.assert_qmp(event, 'data/device', 'drive0')
774 self.assert_qmp(event, 'data/error', 'Input/output error')
775 self.assert_qmp(event, 'data/offset', self.STREAM_BUFFER_SIZE)
776 self.assert_qmp(event, 'data/len', self.image_len)
777 completed = True
Kevin Wolf1dac83f2018-04-30 19:09:46 +0200778 elif event['event'] == 'JOB_STATUS_CHANGE':
779 self.assert_qmp(event, 'data/id', 'drive0')
Paolo Bonzini90f0b712012-09-28 17:23:02 +0200780
Stefan Hajnocziecc1c882013-05-28 17:11:34 +0200781 self.assert_no_active_block_jobs()
Paolo Bonzini90f0b712012-09-28 17:23:02 +0200782 self.vm.shutdown()
783
784class TestENOSPC(TestErrors):
785 def setUp(self):
786 self.blkdebug_file = backing_img + ".blkdebug"
Stefan Hajnoczi2499a092013-05-28 17:11:37 +0200787 iotests.create_image(backing_img, TestErrors.image_len)
Paolo Bonzini90f0b712012-09-28 17:23:02 +0200788 self.create_blkdebug_file(self.blkdebug_file, "read_aio", 28)
789 qemu_img('create', '-f', iotests.imgfmt,
790 '-o', 'backing_file=blkdebug:%s:%s,backing_fmt=raw'
791 % (self.blkdebug_file, backing_img),
792 test_img)
793 self.vm = iotests.VM().add_drive(test_img)
794 self.vm.launch()
795
796 def tearDown(self):
797 self.vm.shutdown()
798 os.remove(test_img)
799 os.remove(backing_img)
800 os.remove(self.blkdebug_file)
801
802 def test_enospc(self):
Stefan Hajnocziecc1c882013-05-28 17:11:34 +0200803 self.assert_no_active_block_jobs()
Paolo Bonzini90f0b712012-09-28 17:23:02 +0200804
805 result = self.vm.qmp('block-stream', device='drive0', on_error='enospc')
806 self.assert_qmp(result, 'return', {})
807
808 error = False
809 completed = False
810 while not completed:
811 for event in self.vm.get_qmp_events(wait=True):
812 if event['event'] == 'BLOCK_JOB_ERROR':
813 self.assert_qmp(event, 'data/device', 'drive0')
814 self.assert_qmp(event, 'data/operation', 'read')
Max Reitzdca9b6a2017-11-09 21:30:21 +0100815 error = True
Paolo Bonzini90f0b712012-09-28 17:23:02 +0200816
817 result = self.vm.qmp('query-block-jobs')
818 self.assert_qmp(result, 'return[0]/paused', True)
819 self.assert_qmp(result, 'return[0]/offset', self.STREAM_BUFFER_SIZE)
820 self.assert_qmp(result, 'return[0]/io-status', 'nospace')
821
822 result = self.vm.qmp('block-job-resume', device='drive0')
823 self.assert_qmp(result, 'return', {})
824
825 result = self.vm.qmp('query-block-jobs')
Max Reitzdca9b6a2017-11-09 21:30:21 +0100826 if result == {'return': []}:
827 # Race; likely already finished. Check.
828 continue
Paolo Bonzini90f0b712012-09-28 17:23:02 +0200829 self.assert_qmp(result, 'return[0]/paused', False)
830 self.assert_qmp(result, 'return[0]/io-status', 'ok')
Paolo Bonzini90f0b712012-09-28 17:23:02 +0200831 elif event['event'] == 'BLOCK_JOB_COMPLETED':
832 self.assertTrue(error, 'job completed unexpectedly')
833 self.assert_qmp(event, 'data/type', 'stream')
834 self.assert_qmp(event, 'data/device', 'drive0')
835 self.assert_qmp_absent(event, 'data/error')
836 self.assert_qmp(event, 'data/offset', self.image_len)
837 self.assert_qmp(event, 'data/len', self.image_len)
838 completed = True
Kevin Wolf1dac83f2018-04-30 19:09:46 +0200839 elif event['event'] == 'JOB_STATUS_CHANGE':
840 self.assert_qmp(event, 'data/id', 'drive0')
Paolo Bonzini90f0b712012-09-28 17:23:02 +0200841
Stefan Hajnocziecc1c882013-05-28 17:11:34 +0200842 self.assert_no_active_block_jobs()
Paolo Bonzini90f0b712012-09-28 17:23:02 +0200843 self.vm.shutdown()
Stefan Hajnoczi774a8852012-08-28 15:26:49 +0100844
Stefan Hajnoczi2499a092013-05-28 17:11:37 +0200845class TestStreamStop(iotests.QMPTestCase):
Stefan Hajnoczi37ce63e2012-02-29 13:25:22 +0000846 image_len = 8 * 1024 * 1024 * 1024 # GB
847
848 def setUp(self):
849 qemu_img('create', backing_img, str(TestStreamStop.image_len))
Kevin Wolf90c9b162014-11-20 16:27:08 +0100850 qemu_io('-f', 'raw', '-c', 'write -P 0x1 0 32M', backing_img)
Stefan Hajnoczi37ce63e2012-02-29 13:25:22 +0000851 qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img)
Kevin Wolf90c9b162014-11-20 16:27:08 +0100852 qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0x1 32M 32M', test_img)
Fam Zhengb59b3d52013-11-20 10:01:56 +0800853 self.vm = iotests.VM().add_drive("blkdebug::" + test_img)
Stefan Hajnoczi37ce63e2012-02-29 13:25:22 +0000854 self.vm.launch()
855
856 def tearDown(self):
857 self.vm.shutdown()
858 os.remove(test_img)
859 os.remove(backing_img)
860
861 def test_stream_stop(self):
Stefan Hajnocziecc1c882013-05-28 17:11:34 +0200862 self.assert_no_active_block_jobs()
Stefan Hajnoczi37ce63e2012-02-29 13:25:22 +0000863
Fam Zhengb59b3d52013-11-20 10:01:56 +0800864 self.vm.pause_drive('drive0')
Stefan Hajnoczidb58f9c2012-04-11 16:27:10 +0100865 result = self.vm.qmp('block-stream', device='drive0')
Stefan Hajnoczi37ce63e2012-02-29 13:25:22 +0000866 self.assert_qmp(result, 'return', {})
867
Paolo Bonzini0fd05e82012-06-06 16:23:27 +0200868 time.sleep(0.1)
Stefan Hajnoczi37ce63e2012-02-29 13:25:22 +0000869 events = self.vm.get_qmp_events(wait=False)
Kevin Wolf1dac83f2018-04-30 19:09:46 +0200870 for e in events:
871 self.assert_qmp(e, 'event', 'JOB_STATUS_CHANGE')
872 self.assert_qmp(e, 'data/id', 'drive0')
Stefan Hajnoczi37ce63e2012-02-29 13:25:22 +0000873
Fam Zhengb59b3d52013-11-20 10:01:56 +0800874 self.cancel_and_wait(resume=True)
Stefan Hajnoczi37ce63e2012-02-29 13:25:22 +0000875
Stefan Hajnoczi2499a092013-05-28 17:11:37 +0200876class TestSetSpeed(iotests.QMPTestCase):
Stefan Hajnoczi37ce63e2012-02-29 13:25:22 +0000877 image_len = 80 * 1024 * 1024 # MB
878
879 def setUp(self):
880 qemu_img('create', backing_img, str(TestSetSpeed.image_len))
Kevin Wolf90c9b162014-11-20 16:27:08 +0100881 qemu_io('-f', 'raw', '-c', 'write -P 0x1 0 32M', backing_img)
Stefan Hajnoczi37ce63e2012-02-29 13:25:22 +0000882 qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img)
Kevin Wolf90c9b162014-11-20 16:27:08 +0100883 qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0x1 32M 32M', test_img)
Fam Zhengb59b3d52013-11-20 10:01:56 +0800884 self.vm = iotests.VM().add_drive('blkdebug::' + test_img)
Stefan Hajnoczi37ce63e2012-02-29 13:25:22 +0000885 self.vm.launch()
886
887 def tearDown(self):
888 self.vm.shutdown()
889 os.remove(test_img)
890 os.remove(backing_img)
891
Stefan Hajnoczie4253062012-04-25 16:51:04 +0100892 # This is a short performance test which is not run by default.
893 # Invoke "IMGFMT=qed ./030 TestSetSpeed.perf_test_throughput"
894 def perf_test_throughput(self):
Stefan Hajnocziecc1c882013-05-28 17:11:34 +0200895 self.assert_no_active_block_jobs()
Stefan Hajnoczi37ce63e2012-02-29 13:25:22 +0000896
Stefan Hajnoczidb58f9c2012-04-11 16:27:10 +0100897 result = self.vm.qmp('block-stream', device='drive0')
Stefan Hajnoczi37ce63e2012-02-29 13:25:22 +0000898 self.assert_qmp(result, 'return', {})
899
Stefan Hajnoczie4253062012-04-25 16:51:04 +0100900 result = self.vm.qmp('block-job-set-speed', device='drive0', speed=8 * 1024 * 1024)
Stefan Hajnoczi37ce63e2012-02-29 13:25:22 +0000901 self.assert_qmp(result, 'return', {})
902
Fam Zheng9974ad42014-04-02 13:54:07 +0800903 self.wait_until_completed()
Stefan Hajnoczi37ce63e2012-02-29 13:25:22 +0000904
Stefan Hajnocziecc1c882013-05-28 17:11:34 +0200905 self.assert_no_active_block_jobs()
Stefan Hajnoczi37ce63e2012-02-29 13:25:22 +0000906
Stefan Hajnoczie4253062012-04-25 16:51:04 +0100907 def test_set_speed(self):
Stefan Hajnocziecc1c882013-05-28 17:11:34 +0200908 self.assert_no_active_block_jobs()
Stefan Hajnoczie4253062012-04-25 16:51:04 +0100909
Fam Zhengb59b3d52013-11-20 10:01:56 +0800910 self.vm.pause_drive('drive0')
Stefan Hajnoczie4253062012-04-25 16:51:04 +0100911 result = self.vm.qmp('block-stream', device='drive0')
912 self.assert_qmp(result, 'return', {})
913
914 # Default speed is 0
915 result = self.vm.qmp('query-block-jobs')
916 self.assert_qmp(result, 'return[0]/device', 'drive0')
917 self.assert_qmp(result, 'return[0]/speed', 0)
918
919 result = self.vm.qmp('block-job-set-speed', device='drive0', speed=8 * 1024 * 1024)
920 self.assert_qmp(result, 'return', {})
921
922 # Ensure the speed we set was accepted
923 result = self.vm.qmp('query-block-jobs')
924 self.assert_qmp(result, 'return[0]/device', 'drive0')
925 self.assert_qmp(result, 'return[0]/speed', 8 * 1024 * 1024)
926
Fam Zhengb59b3d52013-11-20 10:01:56 +0800927 self.cancel_and_wait(resume=True)
928 self.vm.pause_drive('drive0')
Stefan Hajnoczie4253062012-04-25 16:51:04 +0100929
930 # Check setting speed in block-stream works
931 result = self.vm.qmp('block-stream', device='drive0', speed=4 * 1024 * 1024)
932 self.assert_qmp(result, 'return', {})
933
934 result = self.vm.qmp('query-block-jobs')
935 self.assert_qmp(result, 'return[0]/device', 'drive0')
936 self.assert_qmp(result, 'return[0]/speed', 4 * 1024 * 1024)
937
Fam Zhengb59b3d52013-11-20 10:01:56 +0800938 self.cancel_and_wait(resume=True)
Stefan Hajnoczie4253062012-04-25 16:51:04 +0100939
940 def test_set_speed_invalid(self):
Stefan Hajnocziecc1c882013-05-28 17:11:34 +0200941 self.assert_no_active_block_jobs()
Stefan Hajnoczie4253062012-04-25 16:51:04 +0100942
943 result = self.vm.qmp('block-stream', device='drive0', speed=-1)
Kevin Wolf1ef7d9d2019-11-26 14:39:55 +0100944 self.assert_qmp(result, 'error/desc', "Parameter 'speed' expects a non-negative value")
Stefan Hajnoczie4253062012-04-25 16:51:04 +0100945
Stefan Hajnocziecc1c882013-05-28 17:11:34 +0200946 self.assert_no_active_block_jobs()
Stefan Hajnoczie4253062012-04-25 16:51:04 +0100947
Max Reitzdca9b6a2017-11-09 21:30:21 +0100948 self.vm.pause_drive('drive0')
Stefan Hajnoczie4253062012-04-25 16:51:04 +0100949 result = self.vm.qmp('block-stream', device='drive0')
950 self.assert_qmp(result, 'return', {})
951
952 result = self.vm.qmp('block-job-set-speed', device='drive0', speed=-1)
Kevin Wolf1ef7d9d2019-11-26 14:39:55 +0100953 self.assert_qmp(result, 'error/desc', "Parameter 'speed' expects a non-negative value")
Stefan Hajnoczie4253062012-04-25 16:51:04 +0100954
Max Reitzdca9b6a2017-11-09 21:30:21 +0100955 self.cancel_and_wait(resume=True)
Stefan Hajnoczie4253062012-04-25 16:51:04 +0100956
Stefan Hajnoczi37ce63e2012-02-29 13:25:22 +0000957if __name__ == '__main__':
Max Reitz103cbc72019-09-02 21:33:18 +0200958 iotests.main(supported_fmts=['qcow2', 'qed'],
959 supported_protocols=['file'])