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