]>
Commit | Line | Data |
---|---|---|
44c7ca5e PB |
1 | #!/usr/bin/env python |
2 | # | |
3 | # Tests for image mirroring. | |
4 | # | |
5 | # Copyright (C) 2012 Red Hat, Inc. | |
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 | |
44c7ca5e PB |
25 | |
26 | backing_img = os.path.join(iotests.test_dir, 'backing.img') | |
27 | target_backing_img = os.path.join(iotests.test_dir, 'target-backing.img') | |
28 | test_img = os.path.join(iotests.test_dir, 'test.img') | |
29 | target_img = os.path.join(iotests.test_dir, 'target.img') | |
30 | ||
31 | class ImageMirroringTestCase(iotests.QMPTestCase): | |
32 | '''Abstract base class for image mirroring test cases''' | |
33 | ||
2575fe16 SH |
34 | def wait_ready(self, drive='drive0'): |
35 | '''Wait until a block job BLOCK_JOB_READY event''' | |
36 | ready = False | |
37 | while not ready: | |
44c7ca5e | 38 | for event in self.vm.get_qmp_events(wait=True): |
2575fe16 | 39 | if event['event'] == 'BLOCK_JOB_READY': |
44c7ca5e PB |
40 | self.assert_qmp(event, 'data/type', 'mirror') |
41 | self.assert_qmp(event, 'data/device', drive) | |
2575fe16 | 42 | ready = True |
44c7ca5e | 43 | |
2575fe16 SH |
44 | def wait_ready_and_cancel(self, drive='drive0'): |
45 | self.wait_ready(drive) | |
46 | event = self.cancel_and_wait() | |
47 | self.assertEquals(event['event'], 'BLOCK_JOB_COMPLETED') | |
48 | self.assert_qmp(event, 'data/type', 'mirror') | |
49 | self.assert_qmp(event, 'data/offset', self.image_len) | |
50 | self.assert_qmp(event, 'data/len', self.image_len) | |
44c7ca5e PB |
51 | |
52 | def complete_and_wait(self, drive='drive0', wait_ready=True): | |
53 | '''Complete a block job and wait for it to finish''' | |
54 | if wait_ready: | |
2575fe16 | 55 | self.wait_ready() |
44c7ca5e PB |
56 | |
57 | result = self.vm.qmp('block-job-complete', device=drive) | |
58 | self.assert_qmp(result, 'return', {}) | |
59 | ||
0dbe8a1b SH |
60 | event = self.wait_until_completed() |
61 | self.assert_qmp(event, 'data/type', 'mirror') | |
44c7ca5e | 62 | |
44c7ca5e PB |
63 | class TestSingleDrive(ImageMirroringTestCase): |
64 | image_len = 1 * 1024 * 1024 # MB | |
65 | ||
66 | def setUp(self): | |
2499a096 | 67 | iotests.create_image(backing_img, TestSingleDrive.image_len) |
44c7ca5e PB |
68 | qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img) |
69 | self.vm = iotests.VM().add_drive(test_img) | |
70 | self.vm.launch() | |
71 | ||
72 | def tearDown(self): | |
73 | self.vm.shutdown() | |
74 | os.remove(test_img) | |
75 | os.remove(backing_img) | |
76 | try: | |
77 | os.remove(target_img) | |
78 | except OSError: | |
79 | pass | |
80 | ||
81 | def test_complete(self): | |
ecc1c88e | 82 | self.assert_no_active_block_jobs() |
44c7ca5e PB |
83 | |
84 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
85 | target=target_img) | |
86 | self.assert_qmp(result, 'return', {}) | |
87 | ||
88 | self.complete_and_wait() | |
89 | result = self.vm.qmp('query-block') | |
90 | self.assert_qmp(result, 'return[0]/inserted/file', target_img) | |
91 | self.vm.shutdown() | |
3a3918c3 | 92 | self.assertTrue(iotests.compare_images(test_img, target_img), |
44c7ca5e PB |
93 | 'target image does not match source after mirroring') |
94 | ||
95 | def test_cancel(self): | |
ecc1c88e | 96 | self.assert_no_active_block_jobs() |
44c7ca5e PB |
97 | |
98 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
99 | target=target_img) | |
100 | self.assert_qmp(result, 'return', {}) | |
101 | ||
2575fe16 | 102 | self.cancel_and_wait(force=True) |
44c7ca5e PB |
103 | result = self.vm.qmp('query-block') |
104 | self.assert_qmp(result, 'return[0]/inserted/file', test_img) | |
105 | self.vm.shutdown() | |
106 | ||
107 | def test_cancel_after_ready(self): | |
ecc1c88e | 108 | self.assert_no_active_block_jobs() |
44c7ca5e PB |
109 | |
110 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
111 | target=target_img) | |
112 | self.assert_qmp(result, 'return', {}) | |
113 | ||
2575fe16 | 114 | self.wait_ready_and_cancel() |
44c7ca5e PB |
115 | result = self.vm.qmp('query-block') |
116 | self.assert_qmp(result, 'return[0]/inserted/file', test_img) | |
117 | self.vm.shutdown() | |
3a3918c3 | 118 | self.assertTrue(iotests.compare_images(test_img, target_img), |
44c7ca5e PB |
119 | 'target image does not match source after mirroring') |
120 | ||
121 | def test_pause(self): | |
ecc1c88e | 122 | self.assert_no_active_block_jobs() |
44c7ca5e PB |
123 | |
124 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
125 | target=target_img) | |
126 | self.assert_qmp(result, 'return', {}) | |
127 | ||
128 | result = self.vm.qmp('block-job-pause', device='drive0') | |
129 | self.assert_qmp(result, 'return', {}) | |
130 | ||
131 | time.sleep(1) | |
132 | result = self.vm.qmp('query-block-jobs') | |
133 | offset = self.dictpath(result, 'return[0]/offset') | |
134 | ||
135 | time.sleep(1) | |
136 | result = self.vm.qmp('query-block-jobs') | |
137 | self.assert_qmp(result, 'return[0]/offset', offset) | |
138 | ||
139 | result = self.vm.qmp('block-job-resume', device='drive0') | |
140 | self.assert_qmp(result, 'return', {}) | |
141 | ||
142 | self.complete_and_wait() | |
143 | self.vm.shutdown() | |
3a3918c3 | 144 | self.assertTrue(iotests.compare_images(test_img, target_img), |
08e4ed6c PB |
145 | 'target image does not match source after mirroring') |
146 | ||
147 | def test_small_buffer(self): | |
ecc1c88e | 148 | self.assert_no_active_block_jobs() |
08e4ed6c PB |
149 | |
150 | # A small buffer is rounded up automatically | |
151 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
152 | buf_size=4096, target=target_img) | |
153 | self.assert_qmp(result, 'return', {}) | |
154 | ||
155 | self.complete_and_wait() | |
156 | result = self.vm.qmp('query-block') | |
157 | self.assert_qmp(result, 'return[0]/inserted/file', target_img) | |
158 | self.vm.shutdown() | |
3a3918c3 | 159 | self.assertTrue(iotests.compare_images(test_img, target_img), |
08e4ed6c PB |
160 | 'target image does not match source after mirroring') |
161 | ||
162 | def test_small_buffer2(self): | |
ecc1c88e | 163 | self.assert_no_active_block_jobs() |
08e4ed6c PB |
164 | |
165 | qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,size=%d' | |
166 | % (TestSingleDrive.image_len, TestSingleDrive.image_len), target_img) | |
167 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
168 | buf_size=65536, mode='existing', target=target_img) | |
169 | self.assert_qmp(result, 'return', {}) | |
170 | ||
171 | self.complete_and_wait() | |
172 | result = self.vm.qmp('query-block') | |
173 | self.assert_qmp(result, 'return[0]/inserted/file', target_img) | |
174 | self.vm.shutdown() | |
3a3918c3 | 175 | self.assertTrue(iotests.compare_images(test_img, target_img), |
44c7ca5e PB |
176 | 'target image does not match source after mirroring') |
177 | ||
178 | def test_large_cluster(self): | |
ecc1c88e | 179 | self.assert_no_active_block_jobs() |
44c7ca5e PB |
180 | |
181 | qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,backing_file=%s' | |
182 | % (TestSingleDrive.image_len, backing_img), target_img) | |
183 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
184 | mode='existing', target=target_img) | |
185 | self.assert_qmp(result, 'return', {}) | |
186 | ||
187 | self.complete_and_wait() | |
188 | result = self.vm.qmp('query-block') | |
189 | self.assert_qmp(result, 'return[0]/inserted/file', target_img) | |
190 | self.vm.shutdown() | |
3a3918c3 | 191 | self.assertTrue(iotests.compare_images(test_img, target_img), |
44c7ca5e PB |
192 | 'target image does not match source after mirroring') |
193 | ||
194 | def test_medium_not_found(self): | |
195 | result = self.vm.qmp('drive-mirror', device='ide1-cd0', sync='full', | |
196 | target=target_img) | |
197 | self.assert_qmp(result, 'error/class', 'GenericError') | |
198 | ||
199 | def test_image_not_found(self): | |
200 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
201 | mode='existing', target=target_img) | |
202 | self.assert_qmp(result, 'error/class', 'GenericError') | |
203 | ||
204 | def test_device_not_found(self): | |
205 | result = self.vm.qmp('drive-mirror', device='nonexistent', sync='full', | |
206 | target=target_img) | |
207 | self.assert_qmp(result, 'error/class', 'DeviceNotFound') | |
208 | ||
209 | class TestMirrorNoBacking(ImageMirroringTestCase): | |
210 | image_len = 2 * 1024 * 1024 # MB | |
211 | ||
212 | def complete_and_wait(self, drive='drive0', wait_ready=True): | |
2499a096 | 213 | iotests.create_image(target_backing_img, TestMirrorNoBacking.image_len) |
44c7ca5e PB |
214 | return ImageMirroringTestCase.complete_and_wait(self, drive, wait_ready) |
215 | ||
216 | def compare_images(self, img1, img2): | |
2499a096 | 217 | iotests.create_image(target_backing_img, TestMirrorNoBacking.image_len) |
3a3918c3 | 218 | return iotests.compare_images(img1, img2) |
44c7ca5e PB |
219 | |
220 | def setUp(self): | |
2499a096 | 221 | iotests.create_image(backing_img, TestMirrorNoBacking.image_len) |
44c7ca5e PB |
222 | qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img) |
223 | self.vm = iotests.VM().add_drive(test_img) | |
224 | self.vm.launch() | |
225 | ||
226 | def tearDown(self): | |
227 | self.vm.shutdown() | |
228 | os.remove(test_img) | |
229 | os.remove(backing_img) | |
230 | os.remove(target_backing_img) | |
231 | os.remove(target_img) | |
232 | ||
233 | def test_complete(self): | |
ecc1c88e | 234 | self.assert_no_active_block_jobs() |
44c7ca5e PB |
235 | |
236 | qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, target_img) | |
237 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
238 | mode='existing', target=target_img) | |
239 | self.assert_qmp(result, 'return', {}) | |
240 | ||
241 | self.complete_and_wait() | |
242 | result = self.vm.qmp('query-block') | |
243 | self.assert_qmp(result, 'return[0]/inserted/file', target_img) | |
244 | self.vm.shutdown() | |
245 | self.assertTrue(self.compare_images(test_img, target_img), | |
246 | 'target image does not match source after mirroring') | |
247 | ||
248 | def test_cancel(self): | |
ecc1c88e | 249 | self.assert_no_active_block_jobs() |
44c7ca5e PB |
250 | |
251 | qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, target_img) | |
252 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
253 | mode='existing', target=target_img) | |
254 | self.assert_qmp(result, 'return', {}) | |
255 | ||
2575fe16 | 256 | self.wait_ready_and_cancel() |
44c7ca5e PB |
257 | result = self.vm.qmp('query-block') |
258 | self.assert_qmp(result, 'return[0]/inserted/file', test_img) | |
259 | self.vm.shutdown() | |
260 | self.assertTrue(self.compare_images(test_img, target_img), | |
261 | 'target image does not match source after mirroring') | |
262 | ||
b812f671 | 263 | def test_large_cluster(self): |
ecc1c88e | 264 | self.assert_no_active_block_jobs() |
b812f671 PB |
265 | |
266 | # qemu-img create fails if the image is not there | |
267 | qemu_img('create', '-f', iotests.imgfmt, '-o', 'size=%d' | |
268 | %(TestMirrorNoBacking.image_len), target_backing_img) | |
269 | qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,backing_file=%s' | |
270 | % (TestMirrorNoBacking.image_len, target_backing_img), target_img) | |
271 | os.remove(target_backing_img) | |
272 | ||
273 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
274 | mode='existing', target=target_img) | |
275 | self.assert_qmp(result, 'return', {}) | |
276 | ||
277 | self.complete_and_wait() | |
278 | result = self.vm.qmp('query-block') | |
279 | self.assert_qmp(result, 'return[0]/inserted/file', target_img) | |
280 | self.vm.shutdown() | |
281 | self.assertTrue(self.compare_images(test_img, target_img), | |
282 | 'target image does not match source after mirroring') | |
283 | ||
a04eca10 VI |
284 | class TestMirrorResized(ImageMirroringTestCase): |
285 | backing_len = 1 * 1024 * 1024 # MB | |
286 | image_len = 2 * 1024 * 1024 # MB | |
287 | ||
288 | def setUp(self): | |
2499a096 | 289 | iotests.create_image(backing_img, TestMirrorResized.backing_len) |
a04eca10 VI |
290 | qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img) |
291 | qemu_img('resize', test_img, '2M') | |
292 | self.vm = iotests.VM().add_drive(test_img) | |
293 | self.vm.launch() | |
294 | ||
295 | def tearDown(self): | |
296 | self.vm.shutdown() | |
297 | os.remove(test_img) | |
298 | os.remove(backing_img) | |
299 | try: | |
300 | os.remove(target_img) | |
301 | except OSError: | |
302 | pass | |
303 | ||
304 | def test_complete_top(self): | |
ecc1c88e | 305 | self.assert_no_active_block_jobs() |
a04eca10 VI |
306 | |
307 | result = self.vm.qmp('drive-mirror', device='drive0', sync='top', | |
308 | target=target_img) | |
309 | self.assert_qmp(result, 'return', {}) | |
310 | ||
311 | self.complete_and_wait() | |
312 | result = self.vm.qmp('query-block') | |
313 | self.assert_qmp(result, 'return[0]/inserted/file', target_img) | |
314 | self.vm.shutdown() | |
3a3918c3 | 315 | self.assertTrue(iotests.compare_images(test_img, target_img), |
a04eca10 VI |
316 | 'target image does not match source after mirroring') |
317 | ||
318 | def test_complete_full(self): | |
ecc1c88e | 319 | self.assert_no_active_block_jobs() |
a04eca10 VI |
320 | |
321 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
322 | target=target_img) | |
323 | self.assert_qmp(result, 'return', {}) | |
324 | ||
325 | self.complete_and_wait() | |
326 | result = self.vm.qmp('query-block') | |
327 | self.assert_qmp(result, 'return[0]/inserted/file', target_img) | |
328 | self.vm.shutdown() | |
3a3918c3 | 329 | self.assertTrue(iotests.compare_images(test_img, target_img), |
a04eca10 VI |
330 | 'target image does not match source after mirroring') |
331 | ||
9dfa9f59 PB |
332 | class TestReadErrors(ImageMirroringTestCase): |
333 | image_len = 2 * 1024 * 1024 # MB | |
334 | ||
335 | # this should be a multiple of twice the default granularity | |
336 | # so that we hit this offset first in state 1 | |
337 | MIRROR_GRANULARITY = 1024 * 1024 | |
338 | ||
339 | def create_blkdebug_file(self, name, event, errno): | |
340 | file = open(name, 'w') | |
341 | file.write(''' | |
342 | [inject-error] | |
343 | state = "1" | |
344 | event = "%s" | |
345 | errno = "%d" | |
346 | immediately = "off" | |
347 | once = "on" | |
348 | sector = "%d" | |
349 | ||
350 | [set-state] | |
351 | state = "1" | |
352 | event = "%s" | |
353 | new_state = "2" | |
354 | ||
355 | [set-state] | |
356 | state = "2" | |
357 | event = "%s" | |
358 | new_state = "1" | |
359 | ''' % (event, errno, self.MIRROR_GRANULARITY / 512, event, event)) | |
360 | file.close() | |
361 | ||
362 | def setUp(self): | |
363 | self.blkdebug_file = backing_img + ".blkdebug" | |
2499a096 | 364 | iotests.create_image(backing_img, TestReadErrors.image_len) |
9dfa9f59 PB |
365 | self.create_blkdebug_file(self.blkdebug_file, "read_aio", 5) |
366 | qemu_img('create', '-f', iotests.imgfmt, | |
367 | '-o', 'backing_file=blkdebug:%s:%s,backing_fmt=raw' | |
368 | % (self.blkdebug_file, backing_img), | |
369 | test_img) | |
b812f671 PB |
370 | # Write something for tests that use sync='top' |
371 | qemu_io('-c', 'write %d 512' % (self.MIRROR_GRANULARITY + 65536), | |
372 | test_img) | |
9dfa9f59 PB |
373 | self.vm = iotests.VM().add_drive(test_img) |
374 | self.vm.launch() | |
375 | ||
376 | def tearDown(self): | |
377 | self.vm.shutdown() | |
378 | os.remove(test_img) | |
379 | os.remove(backing_img) | |
380 | os.remove(self.blkdebug_file) | |
381 | ||
382 | def test_report_read(self): | |
ecc1c88e | 383 | self.assert_no_active_block_jobs() |
9dfa9f59 PB |
384 | |
385 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
386 | target=target_img) | |
387 | self.assert_qmp(result, 'return', {}) | |
388 | ||
389 | completed = False | |
390 | error = False | |
391 | while not completed: | |
392 | for event in self.vm.get_qmp_events(wait=True): | |
393 | if event['event'] == 'BLOCK_JOB_ERROR': | |
394 | self.assert_qmp(event, 'data/device', 'drive0') | |
395 | self.assert_qmp(event, 'data/operation', 'read') | |
396 | error = True | |
397 | elif event['event'] == 'BLOCK_JOB_READY': | |
398 | self.assertTrue(False, 'job completed unexpectedly') | |
399 | elif event['event'] == 'BLOCK_JOB_COMPLETED': | |
400 | self.assertTrue(error, 'job completed unexpectedly') | |
401 | self.assert_qmp(event, 'data/type', 'mirror') | |
402 | self.assert_qmp(event, 'data/device', 'drive0') | |
403 | self.assert_qmp(event, 'data/error', 'Input/output error') | |
404 | self.assert_qmp(event, 'data/len', self.image_len) | |
405 | completed = True | |
406 | ||
ecc1c88e | 407 | self.assert_no_active_block_jobs() |
9dfa9f59 PB |
408 | self.vm.shutdown() |
409 | ||
410 | def test_ignore_read(self): | |
ecc1c88e | 411 | self.assert_no_active_block_jobs() |
9dfa9f59 PB |
412 | |
413 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
414 | target=target_img, on_source_error='ignore') | |
415 | self.assert_qmp(result, 'return', {}) | |
416 | ||
417 | event = self.vm.get_qmp_event(wait=True) | |
418 | self.assertEquals(event['event'], 'BLOCK_JOB_ERROR') | |
419 | self.assert_qmp(event, 'data/device', 'drive0') | |
420 | self.assert_qmp(event, 'data/operation', 'read') | |
421 | result = self.vm.qmp('query-block-jobs') | |
422 | self.assert_qmp(result, 'return[0]/paused', False) | |
423 | self.complete_and_wait() | |
424 | self.vm.shutdown() | |
425 | ||
b812f671 | 426 | def test_large_cluster(self): |
ecc1c88e | 427 | self.assert_no_active_block_jobs() |
b812f671 PB |
428 | |
429 | # Test COW into the target image. The first half of the | |
430 | # cluster at MIRROR_GRANULARITY has to be copied from | |
431 | # backing_img, even though sync='top'. | |
432 | qemu_img('create', '-f', iotests.imgfmt, '-ocluster_size=131072,backing_file=%s' %(backing_img), target_img) | |
433 | result = self.vm.qmp('drive-mirror', device='drive0', sync='top', | |
434 | on_source_error='ignore', | |
435 | mode='existing', target=target_img) | |
436 | self.assert_qmp(result, 'return', {}) | |
437 | ||
438 | event = self.vm.get_qmp_event(wait=True) | |
439 | self.assertEquals(event['event'], 'BLOCK_JOB_ERROR') | |
440 | self.assert_qmp(event, 'data/device', 'drive0') | |
441 | self.assert_qmp(event, 'data/operation', 'read') | |
442 | result = self.vm.qmp('query-block-jobs') | |
443 | self.assert_qmp(result, 'return[0]/paused', False) | |
444 | self.complete_and_wait() | |
445 | self.vm.shutdown() | |
446 | ||
447 | # Detach blkdebug to compare images successfully | |
448 | qemu_img('rebase', '-f', iotests.imgfmt, '-u', '-b', backing_img, test_img) | |
3a3918c3 | 449 | self.assertTrue(iotests.compare_images(test_img, target_img), |
b812f671 PB |
450 | 'target image does not match source after mirroring') |
451 | ||
9dfa9f59 | 452 | def test_stop_read(self): |
ecc1c88e | 453 | self.assert_no_active_block_jobs() |
9dfa9f59 PB |
454 | |
455 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
456 | target=target_img, on_source_error='stop') | |
457 | self.assert_qmp(result, 'return', {}) | |
458 | ||
459 | error = False | |
460 | ready = False | |
461 | while not ready: | |
462 | for event in self.vm.get_qmp_events(wait=True): | |
463 | if event['event'] == 'BLOCK_JOB_ERROR': | |
464 | self.assert_qmp(event, 'data/device', 'drive0') | |
465 | self.assert_qmp(event, 'data/operation', 'read') | |
466 | ||
467 | result = self.vm.qmp('query-block-jobs') | |
468 | self.assert_qmp(result, 'return[0]/paused', True) | |
469 | self.assert_qmp(result, 'return[0]/io-status', 'failed') | |
470 | ||
471 | result = self.vm.qmp('block-job-resume', device='drive0') | |
472 | self.assert_qmp(result, 'return', {}) | |
473 | error = True | |
474 | elif event['event'] == 'BLOCK_JOB_READY': | |
475 | self.assertTrue(error, 'job completed unexpectedly') | |
476 | self.assert_qmp(event, 'data/device', 'drive0') | |
477 | ready = True | |
478 | ||
479 | result = self.vm.qmp('query-block-jobs') | |
480 | self.assert_qmp(result, 'return[0]/paused', False) | |
481 | self.assert_qmp(result, 'return[0]/io-status', 'ok') | |
482 | ||
483 | self.complete_and_wait(wait_ready=False) | |
ecc1c88e | 484 | self.assert_no_active_block_jobs() |
9dfa9f59 PB |
485 | self.vm.shutdown() |
486 | ||
487 | class TestWriteErrors(ImageMirroringTestCase): | |
488 | image_len = 2 * 1024 * 1024 # MB | |
489 | ||
490 | # this should be a multiple of twice the default granularity | |
491 | # so that we hit this offset first in state 1 | |
492 | MIRROR_GRANULARITY = 1024 * 1024 | |
493 | ||
494 | def create_blkdebug_file(self, name, event, errno): | |
495 | file = open(name, 'w') | |
496 | file.write(''' | |
497 | [inject-error] | |
498 | state = "1" | |
499 | event = "%s" | |
500 | errno = "%d" | |
501 | immediately = "off" | |
502 | once = "on" | |
503 | sector = "%d" | |
504 | ||
505 | [set-state] | |
506 | state = "1" | |
507 | event = "%s" | |
508 | new_state = "2" | |
509 | ||
510 | [set-state] | |
511 | state = "2" | |
512 | event = "%s" | |
513 | new_state = "1" | |
514 | ''' % (event, errno, self.MIRROR_GRANULARITY / 512, event, event)) | |
515 | file.close() | |
516 | ||
517 | def setUp(self): | |
518 | self.blkdebug_file = target_img + ".blkdebug" | |
2499a096 | 519 | iotests.create_image(backing_img, TestWriteErrors.image_len) |
9dfa9f59 PB |
520 | self.create_blkdebug_file(self.blkdebug_file, "write_aio", 5) |
521 | qemu_img('create', '-f', iotests.imgfmt, '-obacking_file=%s' %(backing_img), test_img) | |
522 | self.vm = iotests.VM().add_drive(test_img) | |
523 | self.target_img = 'blkdebug:%s:%s' % (self.blkdebug_file, target_img) | |
524 | qemu_img('create', '-f', iotests.imgfmt, '-osize=%d' %(TestWriteErrors.image_len), target_img) | |
525 | self.vm.launch() | |
526 | ||
527 | def tearDown(self): | |
528 | self.vm.shutdown() | |
529 | os.remove(test_img) | |
530 | os.remove(backing_img) | |
531 | os.remove(self.blkdebug_file) | |
532 | ||
533 | def test_report_write(self): | |
ecc1c88e | 534 | self.assert_no_active_block_jobs() |
9dfa9f59 PB |
535 | |
536 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
537 | mode='existing', target=self.target_img) | |
538 | self.assert_qmp(result, 'return', {}) | |
539 | ||
540 | completed = False | |
541 | error = False | |
542 | while not completed: | |
543 | for event in self.vm.get_qmp_events(wait=True): | |
544 | if event['event'] == 'BLOCK_JOB_ERROR': | |
545 | self.assert_qmp(event, 'data/device', 'drive0') | |
546 | self.assert_qmp(event, 'data/operation', 'write') | |
547 | error = True | |
548 | elif event['event'] == 'BLOCK_JOB_READY': | |
549 | self.assertTrue(False, 'job completed unexpectedly') | |
550 | elif event['event'] == 'BLOCK_JOB_COMPLETED': | |
551 | self.assertTrue(error, 'job completed unexpectedly') | |
552 | self.assert_qmp(event, 'data/type', 'mirror') | |
553 | self.assert_qmp(event, 'data/device', 'drive0') | |
554 | self.assert_qmp(event, 'data/error', 'Input/output error') | |
555 | self.assert_qmp(event, 'data/len', self.image_len) | |
556 | completed = True | |
557 | ||
ecc1c88e | 558 | self.assert_no_active_block_jobs() |
9dfa9f59 PB |
559 | self.vm.shutdown() |
560 | ||
561 | def test_ignore_write(self): | |
ecc1c88e | 562 | self.assert_no_active_block_jobs() |
9dfa9f59 PB |
563 | |
564 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
565 | mode='existing', target=self.target_img, | |
566 | on_target_error='ignore') | |
567 | self.assert_qmp(result, 'return', {}) | |
568 | ||
569 | event = self.vm.get_qmp_event(wait=True) | |
570 | self.assertEquals(event['event'], 'BLOCK_JOB_ERROR') | |
571 | self.assert_qmp(event, 'data/device', 'drive0') | |
572 | self.assert_qmp(event, 'data/operation', 'write') | |
573 | result = self.vm.qmp('query-block-jobs') | |
574 | self.assert_qmp(result, 'return[0]/paused', False) | |
575 | self.complete_and_wait() | |
576 | self.vm.shutdown() | |
577 | ||
578 | def test_stop_write(self): | |
ecc1c88e | 579 | self.assert_no_active_block_jobs() |
9dfa9f59 PB |
580 | |
581 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
582 | mode='existing', target=self.target_img, | |
583 | on_target_error='stop') | |
584 | self.assert_qmp(result, 'return', {}) | |
585 | ||
586 | error = False | |
587 | ready = False | |
588 | while not ready: | |
589 | for event in self.vm.get_qmp_events(wait=True): | |
590 | if event['event'] == 'BLOCK_JOB_ERROR': | |
591 | self.assert_qmp(event, 'data/device', 'drive0') | |
592 | self.assert_qmp(event, 'data/operation', 'write') | |
593 | ||
594 | result = self.vm.qmp('query-block-jobs') | |
595 | self.assert_qmp(result, 'return[0]/paused', True) | |
596 | self.assert_qmp(result, 'return[0]/io-status', 'failed') | |
597 | ||
598 | result = self.vm.qmp('block-job-resume', device='drive0') | |
599 | self.assert_qmp(result, 'return', {}) | |
600 | ||
601 | result = self.vm.qmp('query-block-jobs') | |
602 | self.assert_qmp(result, 'return[0]/paused', False) | |
603 | self.assert_qmp(result, 'return[0]/io-status', 'ok') | |
604 | error = True | |
605 | elif event['event'] == 'BLOCK_JOB_READY': | |
606 | self.assertTrue(error, 'job completed unexpectedly') | |
607 | self.assert_qmp(event, 'data/device', 'drive0') | |
608 | ready = True | |
609 | ||
610 | self.complete_and_wait(wait_ready=False) | |
ecc1c88e | 611 | self.assert_no_active_block_jobs() |
9dfa9f59 PB |
612 | self.vm.shutdown() |
613 | ||
44c7ca5e PB |
614 | class TestSetSpeed(ImageMirroringTestCase): |
615 | image_len = 80 * 1024 * 1024 # MB | |
616 | ||
617 | def setUp(self): | |
618 | qemu_img('create', backing_img, str(TestSetSpeed.image_len)) | |
619 | qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img) | |
620 | self.vm = iotests.VM().add_drive(test_img) | |
621 | self.vm.launch() | |
622 | ||
623 | def tearDown(self): | |
624 | self.vm.shutdown() | |
625 | os.remove(test_img) | |
626 | os.remove(backing_img) | |
627 | os.remove(target_img) | |
628 | ||
629 | def test_set_speed(self): | |
ecc1c88e | 630 | self.assert_no_active_block_jobs() |
44c7ca5e PB |
631 | |
632 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
633 | target=target_img) | |
634 | self.assert_qmp(result, 'return', {}) | |
635 | ||
636 | # Default speed is 0 | |
637 | result = self.vm.qmp('query-block-jobs') | |
638 | self.assert_qmp(result, 'return[0]/device', 'drive0') | |
639 | self.assert_qmp(result, 'return[0]/speed', 0) | |
640 | ||
641 | result = self.vm.qmp('block-job-set-speed', device='drive0', speed=8 * 1024 * 1024) | |
642 | self.assert_qmp(result, 'return', {}) | |
643 | ||
644 | # Ensure the speed we set was accepted | |
645 | result = self.vm.qmp('query-block-jobs') | |
646 | self.assert_qmp(result, 'return[0]/device', 'drive0') | |
647 | self.assert_qmp(result, 'return[0]/speed', 8 * 1024 * 1024) | |
648 | ||
2575fe16 | 649 | self.wait_ready_and_cancel() |
44c7ca5e PB |
650 | |
651 | # Check setting speed in drive-mirror works | |
652 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
653 | target=target_img, speed=4*1024*1024) | |
654 | self.assert_qmp(result, 'return', {}) | |
655 | ||
656 | result = self.vm.qmp('query-block-jobs') | |
657 | self.assert_qmp(result, 'return[0]/device', 'drive0') | |
658 | self.assert_qmp(result, 'return[0]/speed', 4 * 1024 * 1024) | |
659 | ||
2575fe16 | 660 | self.wait_ready_and_cancel() |
44c7ca5e PB |
661 | |
662 | def test_set_speed_invalid(self): | |
ecc1c88e | 663 | self.assert_no_active_block_jobs() |
44c7ca5e PB |
664 | |
665 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
666 | target=target_img, speed=-1) | |
667 | self.assert_qmp(result, 'error/class', 'GenericError') | |
668 | ||
ecc1c88e | 669 | self.assert_no_active_block_jobs() |
44c7ca5e PB |
670 | |
671 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
672 | target=target_img) | |
673 | self.assert_qmp(result, 'return', {}) | |
674 | ||
675 | result = self.vm.qmp('block-job-set-speed', device='drive0', speed=-1) | |
676 | self.assert_qmp(result, 'error/class', 'GenericError') | |
677 | ||
2575fe16 | 678 | self.wait_ready_and_cancel() |
44c7ca5e | 679 | |
c15badee HR |
680 | class TestUnbackedSource(ImageMirroringTestCase): |
681 | image_len = 2 * 1024 * 1024 # MB | |
682 | ||
683 | def setUp(self): | |
684 | qemu_img('create', '-f', iotests.imgfmt, test_img, | |
685 | str(TestUnbackedSource.image_len)) | |
686 | self.vm = iotests.VM().add_drive(test_img) | |
687 | self.vm.launch() | |
688 | ||
689 | def tearDown(self): | |
690 | self.vm.shutdown() | |
691 | os.remove(test_img) | |
692 | os.remove(target_img) | |
693 | ||
171d6431 HR |
694 | def test_absolute_paths_full(self): |
695 | self.assert_no_active_block_jobs() | |
696 | result = self.vm.qmp('drive-mirror', device='drive0', | |
697 | sync='full', target=target_img, | |
698 | mode='absolute-paths') | |
699 | self.assert_qmp(result, 'return', {}) | |
700 | self.complete_and_wait() | |
701 | self.assert_no_active_block_jobs() | |
702 | ||
703 | def test_absolute_paths_top(self): | |
704 | self.assert_no_active_block_jobs() | |
705 | result = self.vm.qmp('drive-mirror', device='drive0', | |
706 | sync='top', target=target_img, | |
707 | mode='absolute-paths') | |
708 | self.assert_qmp(result, 'return', {}) | |
709 | self.complete_and_wait() | |
c15badee HR |
710 | self.assert_no_active_block_jobs() |
711 | ||
171d6431 HR |
712 | def test_absolute_paths_none(self): |
713 | self.assert_no_active_block_jobs() | |
714 | result = self.vm.qmp('drive-mirror', device='drive0', | |
715 | sync='none', target=target_img, | |
716 | mode='absolute-paths') | |
717 | self.assert_qmp(result, 'return', {}) | |
718 | self.complete_and_wait() | |
719 | self.assert_no_active_block_jobs() | |
c15badee | 720 | |
44c7ca5e PB |
721 | if __name__ == '__main__': |
722 | iotests.main(supported_fmts=['qcow2', 'qed']) |