]>
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 | ||
d88964ae BC |
31 | quorum_img1 = os.path.join(iotests.test_dir, 'quorum1.img') |
32 | quorum_img2 = os.path.join(iotests.test_dir, 'quorum2.img') | |
33 | quorum_img3 = os.path.join(iotests.test_dir, 'quorum3.img') | |
34 | quorum_repair_img = os.path.join(iotests.test_dir, 'quorum_repair.img') | |
35 | quorum_snapshot_file = os.path.join(iotests.test_dir, 'quorum_snapshot.img') | |
36 | ||
866323f3 | 37 | class TestSingleDrive(iotests.QMPTestCase): |
44c7ca5e | 38 | image_len = 1 * 1024 * 1024 # MB |
94ca2c73 FZ |
39 | qmp_cmd = 'drive-mirror' |
40 | qmp_target = target_img | |
41 | not_found_error = 'DeviceNotFound' | |
44c7ca5e PB |
42 | |
43 | def setUp(self): | |
3b9f27d2 | 44 | iotests.create_image(backing_img, self.image_len) |
44c7ca5e PB |
45 | qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img) |
46 | self.vm = iotests.VM().add_drive(test_img) | |
0ed82f7a HR |
47 | if iotests.qemu_default_machine == 'pc': |
48 | self.vm.add_drive(None, 'media=cdrom', 'ide') | |
44c7ca5e PB |
49 | self.vm.launch() |
50 | ||
51 | def tearDown(self): | |
52 | self.vm.shutdown() | |
53 | os.remove(test_img) | |
54 | os.remove(backing_img) | |
55 | try: | |
56 | os.remove(target_img) | |
57 | except OSError: | |
58 | pass | |
59 | ||
60 | def test_complete(self): | |
ecc1c88e | 61 | self.assert_no_active_block_jobs() |
44c7ca5e | 62 | |
94ca2c73 FZ |
63 | result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full', |
64 | target=self.qmp_target) | |
44c7ca5e PB |
65 | self.assert_qmp(result, 'return', {}) |
66 | ||
67 | self.complete_and_wait() | |
68 | result = self.vm.qmp('query-block') | |
69 | self.assert_qmp(result, 'return[0]/inserted/file', target_img) | |
70 | self.vm.shutdown() | |
3a3918c3 | 71 | self.assertTrue(iotests.compare_images(test_img, target_img), |
44c7ca5e PB |
72 | 'target image does not match source after mirroring') |
73 | ||
74 | def test_cancel(self): | |
ecc1c88e | 75 | self.assert_no_active_block_jobs() |
44c7ca5e | 76 | |
94ca2c73 FZ |
77 | result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full', |
78 | target=self.qmp_target) | |
44c7ca5e PB |
79 | self.assert_qmp(result, 'return', {}) |
80 | ||
2575fe16 | 81 | self.cancel_and_wait(force=True) |
44c7ca5e PB |
82 | result = self.vm.qmp('query-block') |
83 | self.assert_qmp(result, 'return[0]/inserted/file', test_img) | |
84 | self.vm.shutdown() | |
85 | ||
86 | def test_cancel_after_ready(self): | |
ecc1c88e | 87 | self.assert_no_active_block_jobs() |
44c7ca5e | 88 | |
94ca2c73 FZ |
89 | result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full', |
90 | target=self.qmp_target) | |
44c7ca5e PB |
91 | self.assert_qmp(result, 'return', {}) |
92 | ||
2575fe16 | 93 | self.wait_ready_and_cancel() |
44c7ca5e PB |
94 | result = self.vm.qmp('query-block') |
95 | self.assert_qmp(result, 'return[0]/inserted/file', test_img) | |
96 | self.vm.shutdown() | |
3a3918c3 | 97 | self.assertTrue(iotests.compare_images(test_img, target_img), |
44c7ca5e PB |
98 | 'target image does not match source after mirroring') |
99 | ||
100 | def test_pause(self): | |
ecc1c88e | 101 | self.assert_no_active_block_jobs() |
44c7ca5e | 102 | |
94ca2c73 FZ |
103 | result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full', |
104 | target=self.qmp_target) | |
44c7ca5e PB |
105 | self.assert_qmp(result, 'return', {}) |
106 | ||
107 | result = self.vm.qmp('block-job-pause', device='drive0') | |
108 | self.assert_qmp(result, 'return', {}) | |
109 | ||
110 | time.sleep(1) | |
111 | result = self.vm.qmp('query-block-jobs') | |
112 | offset = self.dictpath(result, 'return[0]/offset') | |
113 | ||
114 | time.sleep(1) | |
115 | result = self.vm.qmp('query-block-jobs') | |
116 | self.assert_qmp(result, 'return[0]/offset', offset) | |
117 | ||
118 | result = self.vm.qmp('block-job-resume', device='drive0') | |
119 | self.assert_qmp(result, 'return', {}) | |
120 | ||
121 | self.complete_and_wait() | |
122 | self.vm.shutdown() | |
3a3918c3 | 123 | self.assertTrue(iotests.compare_images(test_img, target_img), |
08e4ed6c PB |
124 | 'target image does not match source after mirroring') |
125 | ||
126 | def test_small_buffer(self): | |
ecc1c88e | 127 | self.assert_no_active_block_jobs() |
08e4ed6c PB |
128 | |
129 | # A small buffer is rounded up automatically | |
94ca2c73 FZ |
130 | result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full', |
131 | buf_size=4096, target=self.qmp_target) | |
08e4ed6c PB |
132 | self.assert_qmp(result, 'return', {}) |
133 | ||
134 | self.complete_and_wait() | |
135 | result = self.vm.qmp('query-block') | |
136 | self.assert_qmp(result, 'return[0]/inserted/file', target_img) | |
137 | self.vm.shutdown() | |
3a3918c3 | 138 | self.assertTrue(iotests.compare_images(test_img, target_img), |
08e4ed6c PB |
139 | 'target image does not match source after mirroring') |
140 | ||
141 | def test_small_buffer2(self): | |
ecc1c88e | 142 | self.assert_no_active_block_jobs() |
08e4ed6c PB |
143 | |
144 | qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,size=%d' | |
3b9f27d2 | 145 | % (self.image_len, self.image_len), target_img) |
94ca2c73 FZ |
146 | result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full', |
147 | buf_size=65536, mode='existing', target=self.qmp_target) | |
08e4ed6c PB |
148 | self.assert_qmp(result, 'return', {}) |
149 | ||
150 | self.complete_and_wait() | |
151 | result = self.vm.qmp('query-block') | |
152 | self.assert_qmp(result, 'return[0]/inserted/file', target_img) | |
153 | self.vm.shutdown() | |
3a3918c3 | 154 | self.assertTrue(iotests.compare_images(test_img, target_img), |
44c7ca5e PB |
155 | 'target image does not match source after mirroring') |
156 | ||
157 | def test_large_cluster(self): | |
ecc1c88e | 158 | self.assert_no_active_block_jobs() |
44c7ca5e PB |
159 | |
160 | qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,backing_file=%s' | |
3b9f27d2 | 161 | % (self.image_len, backing_img), target_img) |
94ca2c73 FZ |
162 | result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full', |
163 | mode='existing', target=self.qmp_target) | |
44c7ca5e PB |
164 | self.assert_qmp(result, 'return', {}) |
165 | ||
166 | self.complete_and_wait() | |
167 | result = self.vm.qmp('query-block') | |
168 | self.assert_qmp(result, 'return[0]/inserted/file', target_img) | |
169 | self.vm.shutdown() | |
3a3918c3 | 170 | self.assertTrue(iotests.compare_images(test_img, target_img), |
44c7ca5e PB |
171 | 'target image does not match source after mirroring') |
172 | ||
173 | def test_medium_not_found(self): | |
d8683155 BT |
174 | if iotests.qemu_default_machine != 'pc': |
175 | return | |
176 | ||
94ca2c73 FZ |
177 | result = self.vm.qmp(self.qmp_cmd, device='ide1-cd0', sync='full', |
178 | target=self.qmp_target) | |
179 | self.assert_qmp(result, 'error/class', self.not_found_error) | |
44c7ca5e PB |
180 | |
181 | def test_image_not_found(self): | |
94ca2c73 FZ |
182 | result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full', |
183 | mode='existing', target=self.qmp_target) | |
44c7ca5e PB |
184 | self.assert_qmp(result, 'error/class', 'GenericError') |
185 | ||
186 | def test_device_not_found(self): | |
94ca2c73 FZ |
187 | result = self.vm.qmp(self.qmp_cmd, device='nonexistent', sync='full', |
188 | target=self.qmp_target) | |
189 | self.assert_qmp(result, 'error/class', self.not_found_error) | |
190 | ||
191 | class TestSingleBlockdev(TestSingleDrive): | |
192 | qmp_cmd = 'blockdev-mirror' | |
193 | qmp_target = 'node1' | |
194 | not_found_error = 'GenericError' | |
195 | ||
196 | def setUp(self): | |
197 | TestSingleDrive.setUp(self) | |
198 | qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, target_img) | |
199 | args = {'options': | |
200 | {'driver': iotests.imgfmt, | |
201 | 'node-name': self.qmp_target, | |
202 | 'file': { 'filename': target_img, 'driver': 'file' } } } | |
203 | result = self.vm.qmp("blockdev-add", **args) | |
204 | self.assert_qmp(result, 'return', {}) | |
205 | ||
206 | test_large_cluster = None | |
207 | test_image_not_found = None | |
208 | test_small_buffer2 = None | |
209 | ||
3b9f27d2 FZ |
210 | class TestSingleDriveZeroLength(TestSingleDrive): |
211 | image_len = 0 | |
212 | test_small_buffer2 = None | |
213 | test_large_cluster = None | |
214 | ||
94ca2c73 FZ |
215 | class TestSingleBlockdevZeroLength(TestSingleBlockdev): |
216 | image_len = 0 | |
217 | ||
5a0f6fd5 KW |
218 | class TestSingleDriveUnalignedLength(TestSingleDrive): |
219 | image_len = 1025 * 1024 | |
220 | test_small_buffer2 = None | |
221 | test_large_cluster = None | |
222 | ||
94ca2c73 FZ |
223 | class TestSingleBlockdevUnalignedLength(TestSingleBlockdev): |
224 | image_len = 1025 * 1024 | |
225 | ||
866323f3 | 226 | class TestMirrorNoBacking(iotests.QMPTestCase): |
44c7ca5e PB |
227 | image_len = 2 * 1024 * 1024 # MB |
228 | ||
44c7ca5e | 229 | def setUp(self): |
2499a096 | 230 | iotests.create_image(backing_img, TestMirrorNoBacking.image_len) |
44c7ca5e PB |
231 | qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img) |
232 | self.vm = iotests.VM().add_drive(test_img) | |
233 | self.vm.launch() | |
234 | ||
235 | def tearDown(self): | |
236 | self.vm.shutdown() | |
237 | os.remove(test_img) | |
238 | os.remove(backing_img) | |
866323f3 FZ |
239 | try: |
240 | os.remove(target_backing_img) | |
241 | except: | |
242 | pass | |
44c7ca5e PB |
243 | os.remove(target_img) |
244 | ||
245 | def test_complete(self): | |
ecc1c88e | 246 | self.assert_no_active_block_jobs() |
44c7ca5e PB |
247 | |
248 | qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, target_img) | |
249 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
250 | mode='existing', target=target_img) | |
251 | self.assert_qmp(result, 'return', {}) | |
252 | ||
253 | self.complete_and_wait() | |
254 | result = self.vm.qmp('query-block') | |
255 | self.assert_qmp(result, 'return[0]/inserted/file', target_img) | |
256 | self.vm.shutdown() | |
866323f3 | 257 | self.assertTrue(iotests.compare_images(test_img, target_img), |
44c7ca5e PB |
258 | 'target image does not match source after mirroring') |
259 | ||
260 | def test_cancel(self): | |
ecc1c88e | 261 | self.assert_no_active_block_jobs() |
44c7ca5e PB |
262 | |
263 | qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, target_img) | |
264 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
265 | mode='existing', target=target_img) | |
266 | self.assert_qmp(result, 'return', {}) | |
267 | ||
2575fe16 | 268 | self.wait_ready_and_cancel() |
44c7ca5e PB |
269 | result = self.vm.qmp('query-block') |
270 | self.assert_qmp(result, 'return[0]/inserted/file', test_img) | |
271 | self.vm.shutdown() | |
866323f3 | 272 | self.assertTrue(iotests.compare_images(test_img, target_img), |
44c7ca5e PB |
273 | 'target image does not match source after mirroring') |
274 | ||
b812f671 | 275 | def test_large_cluster(self): |
ecc1c88e | 276 | self.assert_no_active_block_jobs() |
b812f671 PB |
277 | |
278 | # qemu-img create fails if the image is not there | |
279 | qemu_img('create', '-f', iotests.imgfmt, '-o', 'size=%d' | |
280 | %(TestMirrorNoBacking.image_len), target_backing_img) | |
281 | qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,backing_file=%s' | |
282 | % (TestMirrorNoBacking.image_len, target_backing_img), target_img) | |
b812f671 PB |
283 | |
284 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
285 | mode='existing', target=target_img) | |
286 | self.assert_qmp(result, 'return', {}) | |
287 | ||
288 | self.complete_and_wait() | |
289 | result = self.vm.qmp('query-block') | |
290 | self.assert_qmp(result, 'return[0]/inserted/file', target_img) | |
291 | self.vm.shutdown() | |
866323f3 | 292 | self.assertTrue(iotests.compare_images(test_img, target_img), |
b812f671 PB |
293 | 'target image does not match source after mirroring') |
294 | ||
866323f3 | 295 | class TestMirrorResized(iotests.QMPTestCase): |
a04eca10 VI |
296 | backing_len = 1 * 1024 * 1024 # MB |
297 | image_len = 2 * 1024 * 1024 # MB | |
298 | ||
299 | def setUp(self): | |
2499a096 | 300 | iotests.create_image(backing_img, TestMirrorResized.backing_len) |
a04eca10 VI |
301 | qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img) |
302 | qemu_img('resize', test_img, '2M') | |
303 | self.vm = iotests.VM().add_drive(test_img) | |
304 | self.vm.launch() | |
305 | ||
306 | def tearDown(self): | |
307 | self.vm.shutdown() | |
308 | os.remove(test_img) | |
309 | os.remove(backing_img) | |
310 | try: | |
311 | os.remove(target_img) | |
312 | except OSError: | |
313 | pass | |
314 | ||
315 | def test_complete_top(self): | |
ecc1c88e | 316 | self.assert_no_active_block_jobs() |
a04eca10 VI |
317 | |
318 | result = self.vm.qmp('drive-mirror', device='drive0', sync='top', | |
319 | target=target_img) | |
320 | self.assert_qmp(result, 'return', {}) | |
321 | ||
322 | self.complete_and_wait() | |
323 | result = self.vm.qmp('query-block') | |
324 | self.assert_qmp(result, 'return[0]/inserted/file', target_img) | |
325 | self.vm.shutdown() | |
3a3918c3 | 326 | self.assertTrue(iotests.compare_images(test_img, target_img), |
a04eca10 VI |
327 | 'target image does not match source after mirroring') |
328 | ||
329 | def test_complete_full(self): | |
ecc1c88e | 330 | self.assert_no_active_block_jobs() |
a04eca10 VI |
331 | |
332 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
333 | target=target_img) | |
334 | self.assert_qmp(result, 'return', {}) | |
335 | ||
336 | self.complete_and_wait() | |
337 | result = self.vm.qmp('query-block') | |
338 | self.assert_qmp(result, 'return[0]/inserted/file', target_img) | |
339 | self.vm.shutdown() | |
3a3918c3 | 340 | self.assertTrue(iotests.compare_images(test_img, target_img), |
a04eca10 VI |
341 | 'target image does not match source after mirroring') |
342 | ||
866323f3 | 343 | class TestReadErrors(iotests.QMPTestCase): |
9dfa9f59 PB |
344 | image_len = 2 * 1024 * 1024 # MB |
345 | ||
346 | # this should be a multiple of twice the default granularity | |
347 | # so that we hit this offset first in state 1 | |
348 | MIRROR_GRANULARITY = 1024 * 1024 | |
349 | ||
350 | def create_blkdebug_file(self, name, event, errno): | |
351 | file = open(name, 'w') | |
352 | file.write(''' | |
353 | [inject-error] | |
354 | state = "1" | |
355 | event = "%s" | |
356 | errno = "%d" | |
357 | immediately = "off" | |
358 | once = "on" | |
359 | sector = "%d" | |
360 | ||
361 | [set-state] | |
362 | state = "1" | |
363 | event = "%s" | |
364 | new_state = "2" | |
365 | ||
366 | [set-state] | |
367 | state = "2" | |
368 | event = "%s" | |
369 | new_state = "1" | |
370 | ''' % (event, errno, self.MIRROR_GRANULARITY / 512, event, event)) | |
371 | file.close() | |
372 | ||
373 | def setUp(self): | |
374 | self.blkdebug_file = backing_img + ".blkdebug" | |
2499a096 | 375 | iotests.create_image(backing_img, TestReadErrors.image_len) |
9dfa9f59 PB |
376 | self.create_blkdebug_file(self.blkdebug_file, "read_aio", 5) |
377 | qemu_img('create', '-f', iotests.imgfmt, | |
378 | '-o', 'backing_file=blkdebug:%s:%s,backing_fmt=raw' | |
379 | % (self.blkdebug_file, backing_img), | |
380 | test_img) | |
b812f671 PB |
381 | # Write something for tests that use sync='top' |
382 | qemu_io('-c', 'write %d 512' % (self.MIRROR_GRANULARITY + 65536), | |
383 | test_img) | |
9dfa9f59 PB |
384 | self.vm = iotests.VM().add_drive(test_img) |
385 | self.vm.launch() | |
386 | ||
387 | def tearDown(self): | |
388 | self.vm.shutdown() | |
389 | os.remove(test_img) | |
390 | os.remove(backing_img) | |
391 | os.remove(self.blkdebug_file) | |
392 | ||
393 | def test_report_read(self): | |
ecc1c88e | 394 | self.assert_no_active_block_jobs() |
9dfa9f59 PB |
395 | |
396 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
397 | target=target_img) | |
398 | self.assert_qmp(result, 'return', {}) | |
399 | ||
400 | completed = False | |
401 | error = False | |
402 | while not completed: | |
403 | for event in self.vm.get_qmp_events(wait=True): | |
404 | if event['event'] == 'BLOCK_JOB_ERROR': | |
405 | self.assert_qmp(event, 'data/device', 'drive0') | |
406 | self.assert_qmp(event, 'data/operation', 'read') | |
407 | error = True | |
408 | elif event['event'] == 'BLOCK_JOB_READY': | |
409 | self.assertTrue(False, 'job completed unexpectedly') | |
410 | elif event['event'] == 'BLOCK_JOB_COMPLETED': | |
411 | self.assertTrue(error, 'job completed unexpectedly') | |
412 | self.assert_qmp(event, 'data/type', 'mirror') | |
413 | self.assert_qmp(event, 'data/device', 'drive0') | |
414 | self.assert_qmp(event, 'data/error', 'Input/output error') | |
9dfa9f59 PB |
415 | completed = True |
416 | ||
ecc1c88e | 417 | self.assert_no_active_block_jobs() |
9dfa9f59 PB |
418 | self.vm.shutdown() |
419 | ||
420 | def test_ignore_read(self): | |
ecc1c88e | 421 | self.assert_no_active_block_jobs() |
9dfa9f59 PB |
422 | |
423 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
424 | target=target_img, on_source_error='ignore') | |
425 | self.assert_qmp(result, 'return', {}) | |
426 | ||
427 | event = self.vm.get_qmp_event(wait=True) | |
428 | self.assertEquals(event['event'], 'BLOCK_JOB_ERROR') | |
429 | self.assert_qmp(event, 'data/device', 'drive0') | |
430 | self.assert_qmp(event, 'data/operation', 'read') | |
431 | result = self.vm.qmp('query-block-jobs') | |
432 | self.assert_qmp(result, 'return[0]/paused', False) | |
433 | self.complete_and_wait() | |
434 | self.vm.shutdown() | |
435 | ||
b812f671 | 436 | def test_large_cluster(self): |
ecc1c88e | 437 | self.assert_no_active_block_jobs() |
b812f671 PB |
438 | |
439 | # Test COW into the target image. The first half of the | |
440 | # cluster at MIRROR_GRANULARITY has to be copied from | |
441 | # backing_img, even though sync='top'. | |
442 | qemu_img('create', '-f', iotests.imgfmt, '-ocluster_size=131072,backing_file=%s' %(backing_img), target_img) | |
443 | result = self.vm.qmp('drive-mirror', device='drive0', sync='top', | |
444 | on_source_error='ignore', | |
445 | mode='existing', target=target_img) | |
446 | self.assert_qmp(result, 'return', {}) | |
447 | ||
448 | event = self.vm.get_qmp_event(wait=True) | |
449 | self.assertEquals(event['event'], 'BLOCK_JOB_ERROR') | |
450 | self.assert_qmp(event, 'data/device', 'drive0') | |
451 | self.assert_qmp(event, 'data/operation', 'read') | |
452 | result = self.vm.qmp('query-block-jobs') | |
453 | self.assert_qmp(result, 'return[0]/paused', False) | |
454 | self.complete_and_wait() | |
455 | self.vm.shutdown() | |
456 | ||
457 | # Detach blkdebug to compare images successfully | |
458 | qemu_img('rebase', '-f', iotests.imgfmt, '-u', '-b', backing_img, test_img) | |
3a3918c3 | 459 | self.assertTrue(iotests.compare_images(test_img, target_img), |
b812f671 PB |
460 | 'target image does not match source after mirroring') |
461 | ||
9dfa9f59 | 462 | def test_stop_read(self): |
ecc1c88e | 463 | self.assert_no_active_block_jobs() |
9dfa9f59 PB |
464 | |
465 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
466 | target=target_img, on_source_error='stop') | |
467 | self.assert_qmp(result, 'return', {}) | |
468 | ||
469 | error = False | |
470 | ready = False | |
471 | while not ready: | |
472 | for event in self.vm.get_qmp_events(wait=True): | |
473 | if event['event'] == 'BLOCK_JOB_ERROR': | |
474 | self.assert_qmp(event, 'data/device', 'drive0') | |
475 | self.assert_qmp(event, 'data/operation', 'read') | |
476 | ||
477 | result = self.vm.qmp('query-block-jobs') | |
478 | self.assert_qmp(result, 'return[0]/paused', True) | |
479 | self.assert_qmp(result, 'return[0]/io-status', 'failed') | |
480 | ||
481 | result = self.vm.qmp('block-job-resume', device='drive0') | |
482 | self.assert_qmp(result, 'return', {}) | |
483 | error = True | |
484 | elif event['event'] == 'BLOCK_JOB_READY': | |
485 | self.assertTrue(error, 'job completed unexpectedly') | |
486 | self.assert_qmp(event, 'data/device', 'drive0') | |
487 | ready = True | |
488 | ||
489 | result = self.vm.qmp('query-block-jobs') | |
490 | self.assert_qmp(result, 'return[0]/paused', False) | |
491 | self.assert_qmp(result, 'return[0]/io-status', 'ok') | |
492 | ||
493 | self.complete_and_wait(wait_ready=False) | |
ecc1c88e | 494 | self.assert_no_active_block_jobs() |
9dfa9f59 PB |
495 | self.vm.shutdown() |
496 | ||
866323f3 | 497 | class TestWriteErrors(iotests.QMPTestCase): |
9dfa9f59 PB |
498 | image_len = 2 * 1024 * 1024 # MB |
499 | ||
500 | # this should be a multiple of twice the default granularity | |
501 | # so that we hit this offset first in state 1 | |
502 | MIRROR_GRANULARITY = 1024 * 1024 | |
503 | ||
504 | def create_blkdebug_file(self, name, event, errno): | |
505 | file = open(name, 'w') | |
506 | file.write(''' | |
507 | [inject-error] | |
508 | state = "1" | |
509 | event = "%s" | |
510 | errno = "%d" | |
511 | immediately = "off" | |
512 | once = "on" | |
513 | sector = "%d" | |
514 | ||
515 | [set-state] | |
516 | state = "1" | |
517 | event = "%s" | |
518 | new_state = "2" | |
519 | ||
520 | [set-state] | |
521 | state = "2" | |
522 | event = "%s" | |
523 | new_state = "1" | |
524 | ''' % (event, errno, self.MIRROR_GRANULARITY / 512, event, event)) | |
525 | file.close() | |
526 | ||
527 | def setUp(self): | |
528 | self.blkdebug_file = target_img + ".blkdebug" | |
2499a096 | 529 | iotests.create_image(backing_img, TestWriteErrors.image_len) |
9dfa9f59 PB |
530 | self.create_blkdebug_file(self.blkdebug_file, "write_aio", 5) |
531 | qemu_img('create', '-f', iotests.imgfmt, '-obacking_file=%s' %(backing_img), test_img) | |
532 | self.vm = iotests.VM().add_drive(test_img) | |
533 | self.target_img = 'blkdebug:%s:%s' % (self.blkdebug_file, target_img) | |
534 | qemu_img('create', '-f', iotests.imgfmt, '-osize=%d' %(TestWriteErrors.image_len), target_img) | |
535 | self.vm.launch() | |
536 | ||
537 | def tearDown(self): | |
538 | self.vm.shutdown() | |
539 | os.remove(test_img) | |
540 | os.remove(backing_img) | |
541 | os.remove(self.blkdebug_file) | |
542 | ||
543 | def test_report_write(self): | |
ecc1c88e | 544 | self.assert_no_active_block_jobs() |
9dfa9f59 PB |
545 | |
546 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
547 | mode='existing', target=self.target_img) | |
548 | self.assert_qmp(result, 'return', {}) | |
549 | ||
550 | completed = False | |
551 | error = False | |
552 | while not completed: | |
553 | for event in self.vm.get_qmp_events(wait=True): | |
554 | if event['event'] == 'BLOCK_JOB_ERROR': | |
555 | self.assert_qmp(event, 'data/device', 'drive0') | |
556 | self.assert_qmp(event, 'data/operation', 'write') | |
557 | error = True | |
558 | elif event['event'] == 'BLOCK_JOB_READY': | |
559 | self.assertTrue(False, 'job completed unexpectedly') | |
560 | elif event['event'] == 'BLOCK_JOB_COMPLETED': | |
561 | self.assertTrue(error, 'job completed unexpectedly') | |
562 | self.assert_qmp(event, 'data/type', 'mirror') | |
563 | self.assert_qmp(event, 'data/device', 'drive0') | |
564 | self.assert_qmp(event, 'data/error', 'Input/output error') | |
9dfa9f59 PB |
565 | completed = True |
566 | ||
ecc1c88e | 567 | self.assert_no_active_block_jobs() |
9dfa9f59 PB |
568 | self.vm.shutdown() |
569 | ||
570 | def test_ignore_write(self): | |
ecc1c88e | 571 | self.assert_no_active_block_jobs() |
9dfa9f59 PB |
572 | |
573 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
574 | mode='existing', target=self.target_img, | |
575 | on_target_error='ignore') | |
576 | self.assert_qmp(result, 'return', {}) | |
577 | ||
578 | event = self.vm.get_qmp_event(wait=True) | |
579 | self.assertEquals(event['event'], 'BLOCK_JOB_ERROR') | |
580 | self.assert_qmp(event, 'data/device', 'drive0') | |
581 | self.assert_qmp(event, 'data/operation', 'write') | |
582 | result = self.vm.qmp('query-block-jobs') | |
583 | self.assert_qmp(result, 'return[0]/paused', False) | |
584 | self.complete_and_wait() | |
585 | self.vm.shutdown() | |
586 | ||
587 | def test_stop_write(self): | |
ecc1c88e | 588 | self.assert_no_active_block_jobs() |
9dfa9f59 PB |
589 | |
590 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
591 | mode='existing', target=self.target_img, | |
592 | on_target_error='stop') | |
593 | self.assert_qmp(result, 'return', {}) | |
594 | ||
595 | error = False | |
596 | ready = False | |
597 | while not ready: | |
598 | for event in self.vm.get_qmp_events(wait=True): | |
599 | if event['event'] == 'BLOCK_JOB_ERROR': | |
600 | self.assert_qmp(event, 'data/device', 'drive0') | |
601 | self.assert_qmp(event, 'data/operation', 'write') | |
602 | ||
603 | result = self.vm.qmp('query-block-jobs') | |
604 | self.assert_qmp(result, 'return[0]/paused', True) | |
605 | self.assert_qmp(result, 'return[0]/io-status', 'failed') | |
606 | ||
607 | result = self.vm.qmp('block-job-resume', device='drive0') | |
608 | self.assert_qmp(result, 'return', {}) | |
609 | ||
610 | result = self.vm.qmp('query-block-jobs') | |
611 | self.assert_qmp(result, 'return[0]/paused', False) | |
612 | self.assert_qmp(result, 'return[0]/io-status', 'ok') | |
613 | error = True | |
614 | elif event['event'] == 'BLOCK_JOB_READY': | |
615 | self.assertTrue(error, 'job completed unexpectedly') | |
616 | self.assert_qmp(event, 'data/device', 'drive0') | |
617 | ready = True | |
618 | ||
619 | self.complete_and_wait(wait_ready=False) | |
ecc1c88e | 620 | self.assert_no_active_block_jobs() |
9dfa9f59 PB |
621 | self.vm.shutdown() |
622 | ||
866323f3 | 623 | class TestSetSpeed(iotests.QMPTestCase): |
44c7ca5e PB |
624 | image_len = 80 * 1024 * 1024 # MB |
625 | ||
626 | def setUp(self): | |
627 | qemu_img('create', backing_img, str(TestSetSpeed.image_len)) | |
628 | qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img) | |
629 | self.vm = iotests.VM().add_drive(test_img) | |
630 | self.vm.launch() | |
631 | ||
632 | def tearDown(self): | |
633 | self.vm.shutdown() | |
634 | os.remove(test_img) | |
635 | os.remove(backing_img) | |
636 | os.remove(target_img) | |
637 | ||
638 | def test_set_speed(self): | |
ecc1c88e | 639 | self.assert_no_active_block_jobs() |
44c7ca5e PB |
640 | |
641 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
642 | target=target_img) | |
643 | self.assert_qmp(result, 'return', {}) | |
644 | ||
645 | # Default speed is 0 | |
646 | result = self.vm.qmp('query-block-jobs') | |
647 | self.assert_qmp(result, 'return[0]/device', 'drive0') | |
648 | self.assert_qmp(result, 'return[0]/speed', 0) | |
649 | ||
650 | result = self.vm.qmp('block-job-set-speed', device='drive0', speed=8 * 1024 * 1024) | |
651 | self.assert_qmp(result, 'return', {}) | |
652 | ||
653 | # Ensure the speed we set was accepted | |
654 | result = self.vm.qmp('query-block-jobs') | |
655 | self.assert_qmp(result, 'return[0]/device', 'drive0') | |
656 | self.assert_qmp(result, 'return[0]/speed', 8 * 1024 * 1024) | |
657 | ||
2575fe16 | 658 | self.wait_ready_and_cancel() |
44c7ca5e PB |
659 | |
660 | # Check setting speed in drive-mirror works | |
661 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
662 | target=target_img, speed=4*1024*1024) | |
663 | self.assert_qmp(result, 'return', {}) | |
664 | ||
665 | result = self.vm.qmp('query-block-jobs') | |
666 | self.assert_qmp(result, 'return[0]/device', 'drive0') | |
667 | self.assert_qmp(result, 'return[0]/speed', 4 * 1024 * 1024) | |
668 | ||
2575fe16 | 669 | self.wait_ready_and_cancel() |
44c7ca5e PB |
670 | |
671 | def test_set_speed_invalid(self): | |
ecc1c88e | 672 | self.assert_no_active_block_jobs() |
44c7ca5e PB |
673 | |
674 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
675 | target=target_img, speed=-1) | |
676 | self.assert_qmp(result, 'error/class', 'GenericError') | |
677 | ||
ecc1c88e | 678 | self.assert_no_active_block_jobs() |
44c7ca5e PB |
679 | |
680 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
681 | target=target_img) | |
682 | self.assert_qmp(result, 'return', {}) | |
683 | ||
684 | result = self.vm.qmp('block-job-set-speed', device='drive0', speed=-1) | |
685 | self.assert_qmp(result, 'error/class', 'GenericError') | |
686 | ||
2575fe16 | 687 | self.wait_ready_and_cancel() |
44c7ca5e | 688 | |
866323f3 | 689 | class TestUnbackedSource(iotests.QMPTestCase): |
c15badee HR |
690 | image_len = 2 * 1024 * 1024 # MB |
691 | ||
692 | def setUp(self): | |
693 | qemu_img('create', '-f', iotests.imgfmt, test_img, | |
694 | str(TestUnbackedSource.image_len)) | |
695 | self.vm = iotests.VM().add_drive(test_img) | |
696 | self.vm.launch() | |
697 | ||
698 | def tearDown(self): | |
699 | self.vm.shutdown() | |
700 | os.remove(test_img) | |
701 | os.remove(target_img) | |
702 | ||
171d6431 HR |
703 | def test_absolute_paths_full(self): |
704 | self.assert_no_active_block_jobs() | |
705 | result = self.vm.qmp('drive-mirror', device='drive0', | |
706 | sync='full', target=target_img, | |
707 | mode='absolute-paths') | |
708 | self.assert_qmp(result, 'return', {}) | |
709 | self.complete_and_wait() | |
710 | self.assert_no_active_block_jobs() | |
711 | ||
712 | def test_absolute_paths_top(self): | |
713 | self.assert_no_active_block_jobs() | |
714 | result = self.vm.qmp('drive-mirror', device='drive0', | |
715 | sync='top', target=target_img, | |
716 | mode='absolute-paths') | |
717 | self.assert_qmp(result, 'return', {}) | |
718 | self.complete_and_wait() | |
c15badee HR |
719 | self.assert_no_active_block_jobs() |
720 | ||
171d6431 HR |
721 | def test_absolute_paths_none(self): |
722 | self.assert_no_active_block_jobs() | |
723 | result = self.vm.qmp('drive-mirror', device='drive0', | |
724 | sync='none', target=target_img, | |
725 | mode='absolute-paths') | |
726 | self.assert_qmp(result, 'return', {}) | |
727 | self.complete_and_wait() | |
728 | self.assert_no_active_block_jobs() | |
c15badee | 729 | |
866323f3 | 730 | class TestRepairQuorum(iotests.QMPTestCase): |
d88964ae BC |
731 | """ This class test quorum file repair using drive-mirror. |
732 | It's mostly a fork of TestSingleDrive """ | |
733 | image_len = 1 * 1024 * 1024 # MB | |
734 | IMAGES = [ quorum_img1, quorum_img2, quorum_img3 ] | |
735 | ||
a42a1fac BC |
736 | def has_quorum(self): |
737 | return 'quorum' in iotests.qemu_img_pipe('--help') | |
738 | ||
d88964ae BC |
739 | def setUp(self): |
740 | self.vm = iotests.VM() | |
741 | ||
0ed82f7a HR |
742 | if iotests.qemu_default_machine == 'pc': |
743 | self.vm.add_drive(None, 'media=cdrom', 'ide') | |
744 | ||
d88964ae BC |
745 | # Add each individual quorum images |
746 | for i in self.IMAGES: | |
747 | qemu_img('create', '-f', iotests.imgfmt, i, | |
748 | str(TestSingleDrive.image_len)) | |
749 | # Assign a node name to each quorum image in order to manipulate | |
750 | # them | |
751 | opts = "node-name=img%i" % self.IMAGES.index(i) | |
752 | self.vm = self.vm.add_drive(i, opts) | |
753 | ||
754 | self.vm.launch() | |
755 | ||
756 | #assemble the quorum block device from the individual files | |
757 | args = { "options" : { "driver": "quorum", "id": "quorum0", | |
758 | "vote-threshold": 2, "children": [ "img0", "img1", "img2" ] } } | |
a42a1fac BC |
759 | if self.has_quorum(): |
760 | result = self.vm.qmp("blockdev-add", **args) | |
761 | self.assert_qmp(result, 'return', {}) | |
d88964ae BC |
762 | |
763 | ||
764 | def tearDown(self): | |
765 | self.vm.shutdown() | |
766 | for i in self.IMAGES + [ quorum_repair_img ]: | |
767 | # Do a try/except because the test may have deleted some images | |
768 | try: | |
769 | os.remove(i) | |
770 | except OSError: | |
771 | pass | |
772 | ||
773 | def test_complete(self): | |
a42a1fac BC |
774 | if not self.has_quorum(): |
775 | return | |
776 | ||
d88964ae BC |
777 | self.assert_no_active_block_jobs() |
778 | ||
779 | result = self.vm.qmp('drive-mirror', device='quorum0', sync='full', | |
780 | node_name="repair0", | |
781 | replaces="img1", | |
782 | target=quorum_repair_img, format=iotests.imgfmt) | |
783 | self.assert_qmp(result, 'return', {}) | |
784 | ||
785 | self.complete_and_wait(drive="quorum0") | |
e71fc0ba | 786 | self.assert_has_block_node("repair0", quorum_repair_img) |
d88964ae BC |
787 | # TODO: a better test requiring some QEMU infrastructure will be added |
788 | # to check that this file is really driven by quorum | |
789 | self.vm.shutdown() | |
790 | self.assertTrue(iotests.compare_images(quorum_img2, quorum_repair_img), | |
791 | 'target image does not match source after mirroring') | |
792 | ||
793 | def test_cancel(self): | |
a42a1fac BC |
794 | if not self.has_quorum(): |
795 | return | |
796 | ||
d88964ae BC |
797 | self.assert_no_active_block_jobs() |
798 | ||
799 | result = self.vm.qmp('drive-mirror', device='quorum0', sync='full', | |
800 | node_name="repair0", | |
801 | replaces="img1", | |
802 | target=quorum_repair_img, format=iotests.imgfmt) | |
803 | self.assert_qmp(result, 'return', {}) | |
804 | ||
805 | self.cancel_and_wait(drive="quorum0", force=True) | |
806 | # here we check that the last registered quorum file has not been | |
807 | # swapped out and unref | |
e71fc0ba | 808 | self.assert_has_block_node(None, quorum_img3) |
d88964ae BC |
809 | self.vm.shutdown() |
810 | ||
811 | def test_cancel_after_ready(self): | |
a42a1fac BC |
812 | if not self.has_quorum(): |
813 | return | |
814 | ||
d88964ae BC |
815 | self.assert_no_active_block_jobs() |
816 | ||
817 | result = self.vm.qmp('drive-mirror', device='quorum0', sync='full', | |
818 | node_name="repair0", | |
819 | replaces="img1", | |
820 | target=quorum_repair_img, format=iotests.imgfmt) | |
821 | self.assert_qmp(result, 'return', {}) | |
822 | ||
823 | self.wait_ready_and_cancel(drive="quorum0") | |
d88964ae BC |
824 | # here we check that the last registered quorum file has not been |
825 | # swapped out and unref | |
e71fc0ba | 826 | self.assert_has_block_node(None, quorum_img3) |
d88964ae BC |
827 | self.vm.shutdown() |
828 | self.assertTrue(iotests.compare_images(quorum_img2, quorum_repair_img), | |
829 | 'target image does not match source after mirroring') | |
830 | ||
831 | def test_pause(self): | |
a42a1fac BC |
832 | if not self.has_quorum(): |
833 | return | |
834 | ||
d88964ae BC |
835 | self.assert_no_active_block_jobs() |
836 | ||
837 | result = self.vm.qmp('drive-mirror', device='quorum0', sync='full', | |
838 | node_name="repair0", | |
839 | replaces="img1", | |
840 | target=quorum_repair_img, format=iotests.imgfmt) | |
841 | self.assert_qmp(result, 'return', {}) | |
842 | ||
843 | result = self.vm.qmp('block-job-pause', device='quorum0') | |
844 | self.assert_qmp(result, 'return', {}) | |
845 | ||
846 | time.sleep(1) | |
847 | result = self.vm.qmp('query-block-jobs') | |
848 | offset = self.dictpath(result, 'return[0]/offset') | |
849 | ||
850 | time.sleep(1) | |
851 | result = self.vm.qmp('query-block-jobs') | |
852 | self.assert_qmp(result, 'return[0]/offset', offset) | |
853 | ||
854 | result = self.vm.qmp('block-job-resume', device='quorum0') | |
855 | self.assert_qmp(result, 'return', {}) | |
856 | ||
857 | self.complete_and_wait(drive="quorum0") | |
858 | self.vm.shutdown() | |
859 | self.assertTrue(iotests.compare_images(quorum_img2, quorum_repair_img), | |
860 | 'target image does not match source after mirroring') | |
861 | ||
862 | def test_medium_not_found(self): | |
a42a1fac BC |
863 | if not self.has_quorum(): |
864 | return | |
865 | ||
d8683155 BT |
866 | if iotests.qemu_default_machine != 'pc': |
867 | return | |
868 | ||
0ed82f7a HR |
869 | result = self.vm.qmp('drive-mirror', device='drive0', # CD-ROM |
870 | sync='full', | |
d88964ae BC |
871 | node_name='repair0', |
872 | replaces='img1', | |
873 | target=quorum_repair_img, format=iotests.imgfmt) | |
874 | self.assert_qmp(result, 'error/class', 'GenericError') | |
875 | ||
876 | def test_image_not_found(self): | |
a42a1fac BC |
877 | if not self.has_quorum(): |
878 | return | |
879 | ||
d88964ae BC |
880 | result = self.vm.qmp('drive-mirror', device='quorum0', sync='full', |
881 | node_name='repair0', | |
882 | replaces='img1', | |
883 | mode='existing', | |
884 | target=quorum_repair_img, format=iotests.imgfmt) | |
885 | self.assert_qmp(result, 'error/class', 'GenericError') | |
886 | ||
887 | def test_device_not_found(self): | |
a42a1fac BC |
888 | if not self.has_quorum(): |
889 | return | |
890 | ||
d88964ae BC |
891 | result = self.vm.qmp('drive-mirror', device='nonexistent', sync='full', |
892 | node_name='repair0', | |
893 | replaces='img1', | |
894 | target=quorum_repair_img, format=iotests.imgfmt) | |
895 | self.assert_qmp(result, 'error/class', 'DeviceNotFound') | |
896 | ||
897 | def test_wrong_sync_mode(self): | |
a42a1fac BC |
898 | if not self.has_quorum(): |
899 | return | |
900 | ||
d88964ae BC |
901 | result = self.vm.qmp('drive-mirror', device='quorum0', |
902 | node_name='repair0', | |
903 | replaces='img1', | |
904 | target=quorum_repair_img, format=iotests.imgfmt) | |
905 | self.assert_qmp(result, 'error/class', 'GenericError') | |
906 | ||
907 | def test_no_node_name(self): | |
a42a1fac BC |
908 | if not self.has_quorum(): |
909 | return | |
910 | ||
d88964ae BC |
911 | result = self.vm.qmp('drive-mirror', device='quorum0', sync='full', |
912 | replaces='img1', | |
913 | target=quorum_repair_img, format=iotests.imgfmt) | |
914 | self.assert_qmp(result, 'error/class', 'GenericError') | |
915 | ||
67cc32eb | 916 | def test_nonexistent_replaces(self): |
a42a1fac BC |
917 | if not self.has_quorum(): |
918 | return | |
919 | ||
d88964ae BC |
920 | result = self.vm.qmp('drive-mirror', device='quorum0', sync='full', |
921 | node_name='repair0', | |
922 | replaces='img77', | |
923 | target=quorum_repair_img, format=iotests.imgfmt) | |
924 | self.assert_qmp(result, 'error/class', 'GenericError') | |
925 | ||
926 | def test_after_a_quorum_snapshot(self): | |
a42a1fac BC |
927 | if not self.has_quorum(): |
928 | return | |
929 | ||
d88964ae BC |
930 | result = self.vm.qmp('blockdev-snapshot-sync', node_name='img1', |
931 | snapshot_file=quorum_snapshot_file, | |
932 | snapshot_node_name="snap1"); | |
933 | ||
934 | result = self.vm.qmp('drive-mirror', device='quorum0', sync='full', | |
935 | node_name='repair0', | |
936 | replaces="img1", | |
937 | target=quorum_repair_img, format=iotests.imgfmt) | |
938 | self.assert_qmp(result, 'error/class', 'GenericError') | |
939 | ||
940 | result = self.vm.qmp('drive-mirror', device='quorum0', sync='full', | |
941 | node_name='repair0', | |
942 | replaces="snap1", | |
943 | target=quorum_repair_img, format=iotests.imgfmt) | |
944 | self.assert_qmp(result, 'return', {}) | |
945 | ||
946 | self.complete_and_wait(drive="quorum0") | |
e71fc0ba | 947 | self.assert_has_block_node("repair0", quorum_repair_img) |
d88964ae BC |
948 | # TODO: a better test requiring some QEMU infrastructure will be added |
949 | # to check that this file is really driven by quorum | |
950 | self.vm.shutdown() | |
951 | ||
44c7ca5e PB |
952 | if __name__ == '__main__': |
953 | iotests.main(supported_fmts=['qcow2', 'qed']) |