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 TestReadErrors(ImageMirroringTestCase): 348 image_len = 2 * 1024 * 1024 # MB 349 350 # this should be a multiple of twice the default granularity 351 # so that we hit this offset first in state 1 352 MIRROR_GRANULARITY = 1024 * 1024 353 354 def create_blkdebug_file(self, name, event, errno): 355 file = open(name, 'w') 356 file.write(''' 357[inject-error] 358state = "1" 359event = "%s" 360errno = "%d" 361immediately = "off" 362once = "on" 363sector = "%d" 364 365[set-state] 366state = "1" 367event = "%s" 368new_state = "2" 369 370[set-state] 371state = "2" 372event = "%s" 373new_state = "1" 374''' % (event, errno, self.MIRROR_GRANULARITY / 512, event, event)) 375 file.close() 376 377 def setUp(self): 378 self.blkdebug_file = backing_img + ".blkdebug" 379 self.create_image(backing_img, TestReadErrors.image_len) 380 self.create_blkdebug_file(self.blkdebug_file, "read_aio", 5) 381 qemu_img('create', '-f', iotests.imgfmt, 382 '-o', 'backing_file=blkdebug:%s:%s,backing_fmt=raw' 383 % (self.blkdebug_file, backing_img), 384 test_img) 385 # Write something for tests that use sync='top' 386 qemu_io('-c', 'write %d 512' % (self.MIRROR_GRANULARITY + 65536), 387 test_img) 388 self.vm = iotests.VM().add_drive(test_img) 389 self.vm.launch() 390 391 def tearDown(self): 392 self.vm.shutdown() 393 os.remove(test_img) 394 os.remove(backing_img) 395 os.remove(self.blkdebug_file) 396 397 def test_report_read(self): 398 self.assert_no_active_mirrors() 399 400 result = self.vm.qmp('drive-mirror', device='drive0', sync='full', 401 target=target_img) 402 self.assert_qmp(result, 'return', {}) 403 404 completed = False 405 error = False 406 while not completed: 407 for event in self.vm.get_qmp_events(wait=True): 408 if event['event'] == 'BLOCK_JOB_ERROR': 409 self.assert_qmp(event, 'data/device', 'drive0') 410 self.assert_qmp(event, 'data/operation', 'read') 411 error = True 412 elif event['event'] == 'BLOCK_JOB_READY': 413 self.assertTrue(False, 'job completed unexpectedly') 414 elif event['event'] == 'BLOCK_JOB_COMPLETED': 415 self.assertTrue(error, 'job completed unexpectedly') 416 self.assert_qmp(event, 'data/type', 'mirror') 417 self.assert_qmp(event, 'data/device', 'drive0') 418 self.assert_qmp(event, 'data/error', 'Input/output error') 419 self.assert_qmp(event, 'data/len', self.image_len) 420 completed = True 421 422 self.assert_no_active_mirrors() 423 self.vm.shutdown() 424 425 def test_ignore_read(self): 426 self.assert_no_active_mirrors() 427 428 result = self.vm.qmp('drive-mirror', device='drive0', sync='full', 429 target=target_img, on_source_error='ignore') 430 self.assert_qmp(result, 'return', {}) 431 432 event = self.vm.get_qmp_event(wait=True) 433 self.assertEquals(event['event'], 'BLOCK_JOB_ERROR') 434 self.assert_qmp(event, 'data/device', 'drive0') 435 self.assert_qmp(event, 'data/operation', 'read') 436 result = self.vm.qmp('query-block-jobs') 437 self.assert_qmp(result, 'return[0]/paused', False) 438 self.complete_and_wait() 439 self.vm.shutdown() 440 441 def test_large_cluster(self): 442 self.assert_no_active_mirrors() 443 444 # Test COW into the target image. The first half of the 445 # cluster at MIRROR_GRANULARITY has to be copied from 446 # backing_img, even though sync='top'. 447 qemu_img('create', '-f', iotests.imgfmt, '-ocluster_size=131072,backing_file=%s' %(backing_img), target_img) 448 result = self.vm.qmp('drive-mirror', device='drive0', sync='top', 449 on_source_error='ignore', 450 mode='existing', target=target_img) 451 self.assert_qmp(result, 'return', {}) 452 453 event = self.vm.get_qmp_event(wait=True) 454 self.assertEquals(event['event'], 'BLOCK_JOB_ERROR') 455 self.assert_qmp(event, 'data/device', 'drive0') 456 self.assert_qmp(event, 'data/operation', 'read') 457 result = self.vm.qmp('query-block-jobs') 458 self.assert_qmp(result, 'return[0]/paused', False) 459 self.complete_and_wait() 460 self.vm.shutdown() 461 462 # Detach blkdebug to compare images successfully 463 qemu_img('rebase', '-f', iotests.imgfmt, '-u', '-b', backing_img, test_img) 464 self.assertTrue(self.compare_images(test_img, target_img), 465 'target image does not match source after mirroring') 466 467 def test_stop_read(self): 468 self.assert_no_active_mirrors() 469 470 result = self.vm.qmp('drive-mirror', device='drive0', sync='full', 471 target=target_img, on_source_error='stop') 472 self.assert_qmp(result, 'return', {}) 473 474 error = False 475 ready = False 476 while not ready: 477 for event in self.vm.get_qmp_events(wait=True): 478 if event['event'] == 'BLOCK_JOB_ERROR': 479 self.assert_qmp(event, 'data/device', 'drive0') 480 self.assert_qmp(event, 'data/operation', 'read') 481 482 result = self.vm.qmp('query-block-jobs') 483 self.assert_qmp(result, 'return[0]/paused', True) 484 self.assert_qmp(result, 'return[0]/io-status', 'failed') 485 486 result = self.vm.qmp('block-job-resume', device='drive0') 487 self.assert_qmp(result, 'return', {}) 488 error = True 489 elif event['event'] == 'BLOCK_JOB_READY': 490 self.assertTrue(error, 'job completed unexpectedly') 491 self.assert_qmp(event, 'data/device', 'drive0') 492 ready = True 493 494 result = self.vm.qmp('query-block-jobs') 495 self.assert_qmp(result, 'return[0]/paused', False) 496 self.assert_qmp(result, 'return[0]/io-status', 'ok') 497 498 self.complete_and_wait(wait_ready=False) 499 self.assert_no_active_mirrors() 500 self.vm.shutdown() 501 502class TestWriteErrors(ImageMirroringTestCase): 503 image_len = 2 * 1024 * 1024 # MB 504 505 # this should be a multiple of twice the default granularity 506 # so that we hit this offset first in state 1 507 MIRROR_GRANULARITY = 1024 * 1024 508 509 def create_blkdebug_file(self, name, event, errno): 510 file = open(name, 'w') 511 file.write(''' 512[inject-error] 513state = "1" 514event = "%s" 515errno = "%d" 516immediately = "off" 517once = "on" 518sector = "%d" 519 520[set-state] 521state = "1" 522event = "%s" 523new_state = "2" 524 525[set-state] 526state = "2" 527event = "%s" 528new_state = "1" 529''' % (event, errno, self.MIRROR_GRANULARITY / 512, event, event)) 530 file.close() 531 532 def setUp(self): 533 self.blkdebug_file = target_img + ".blkdebug" 534 self.create_image(backing_img, TestWriteErrors.image_len) 535 self.create_blkdebug_file(self.blkdebug_file, "write_aio", 5) 536 qemu_img('create', '-f', iotests.imgfmt, '-obacking_file=%s' %(backing_img), test_img) 537 self.vm = iotests.VM().add_drive(test_img) 538 self.target_img = 'blkdebug:%s:%s' % (self.blkdebug_file, target_img) 539 qemu_img('create', '-f', iotests.imgfmt, '-osize=%d' %(TestWriteErrors.image_len), target_img) 540 self.vm.launch() 541 542 def tearDown(self): 543 self.vm.shutdown() 544 os.remove(test_img) 545 os.remove(backing_img) 546 os.remove(self.blkdebug_file) 547 548 def test_report_write(self): 549 self.assert_no_active_mirrors() 550 551 result = self.vm.qmp('drive-mirror', device='drive0', sync='full', 552 mode='existing', target=self.target_img) 553 self.assert_qmp(result, 'return', {}) 554 555 completed = False 556 error = False 557 while not completed: 558 for event in self.vm.get_qmp_events(wait=True): 559 if event['event'] == 'BLOCK_JOB_ERROR': 560 self.assert_qmp(event, 'data/device', 'drive0') 561 self.assert_qmp(event, 'data/operation', 'write') 562 error = True 563 elif event['event'] == 'BLOCK_JOB_READY': 564 self.assertTrue(False, 'job completed unexpectedly') 565 elif event['event'] == 'BLOCK_JOB_COMPLETED': 566 self.assertTrue(error, 'job completed unexpectedly') 567 self.assert_qmp(event, 'data/type', 'mirror') 568 self.assert_qmp(event, 'data/device', 'drive0') 569 self.assert_qmp(event, 'data/error', 'Input/output error') 570 self.assert_qmp(event, 'data/len', self.image_len) 571 completed = True 572 573 self.assert_no_active_mirrors() 574 self.vm.shutdown() 575 576 def test_ignore_write(self): 577 self.assert_no_active_mirrors() 578 579 result = self.vm.qmp('drive-mirror', device='drive0', sync='full', 580 mode='existing', target=self.target_img, 581 on_target_error='ignore') 582 self.assert_qmp(result, 'return', {}) 583 584 event = self.vm.get_qmp_event(wait=True) 585 self.assertEquals(event['event'], 'BLOCK_JOB_ERROR') 586 self.assert_qmp(event, 'data/device', 'drive0') 587 self.assert_qmp(event, 'data/operation', 'write') 588 result = self.vm.qmp('query-block-jobs') 589 self.assert_qmp(result, 'return[0]/paused', False) 590 self.complete_and_wait() 591 self.vm.shutdown() 592 593 def test_stop_write(self): 594 self.assert_no_active_mirrors() 595 596 result = self.vm.qmp('drive-mirror', device='drive0', sync='full', 597 mode='existing', target=self.target_img, 598 on_target_error='stop') 599 self.assert_qmp(result, 'return', {}) 600 601 error = False 602 ready = False 603 while not ready: 604 for event in self.vm.get_qmp_events(wait=True): 605 if event['event'] == 'BLOCK_JOB_ERROR': 606 self.assert_qmp(event, 'data/device', 'drive0') 607 self.assert_qmp(event, 'data/operation', 'write') 608 609 result = self.vm.qmp('query-block-jobs') 610 self.assert_qmp(result, 'return[0]/paused', True) 611 self.assert_qmp(result, 'return[0]/io-status', 'failed') 612 613 result = self.vm.qmp('block-job-resume', device='drive0') 614 self.assert_qmp(result, 'return', {}) 615 616 result = self.vm.qmp('query-block-jobs') 617 self.assert_qmp(result, 'return[0]/paused', False) 618 self.assert_qmp(result, 'return[0]/io-status', 'ok') 619 error = True 620 elif event['event'] == 'BLOCK_JOB_READY': 621 self.assertTrue(error, 'job completed unexpectedly') 622 self.assert_qmp(event, 'data/device', 'drive0') 623 ready = True 624 625 self.complete_and_wait(wait_ready=False) 626 self.assert_no_active_mirrors() 627 self.vm.shutdown() 628 629class TestSetSpeed(ImageMirroringTestCase): 630 image_len = 80 * 1024 * 1024 # MB 631 632 def setUp(self): 633 qemu_img('create', backing_img, str(TestSetSpeed.image_len)) 634 qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img) 635 self.vm = iotests.VM().add_drive(test_img) 636 self.vm.launch() 637 638 def tearDown(self): 639 self.vm.shutdown() 640 os.remove(test_img) 641 os.remove(backing_img) 642 os.remove(target_img) 643 644 def test_set_speed(self): 645 self.assert_no_active_mirrors() 646 647 result = self.vm.qmp('drive-mirror', device='drive0', sync='full', 648 target=target_img) 649 self.assert_qmp(result, 'return', {}) 650 651 # Default speed is 0 652 result = self.vm.qmp('query-block-jobs') 653 self.assert_qmp(result, 'return[0]/device', 'drive0') 654 self.assert_qmp(result, 'return[0]/speed', 0) 655 656 result = self.vm.qmp('block-job-set-speed', device='drive0', speed=8 * 1024 * 1024) 657 self.assert_qmp(result, 'return', {}) 658 659 # Ensure the speed we set was accepted 660 result = self.vm.qmp('query-block-jobs') 661 self.assert_qmp(result, 'return[0]/device', 'drive0') 662 self.assert_qmp(result, 'return[0]/speed', 8 * 1024 * 1024) 663 664 self.cancel_and_wait() 665 666 # Check setting speed in drive-mirror works 667 result = self.vm.qmp('drive-mirror', device='drive0', sync='full', 668 target=target_img, speed=4*1024*1024) 669 self.assert_qmp(result, 'return', {}) 670 671 result = self.vm.qmp('query-block-jobs') 672 self.assert_qmp(result, 'return[0]/device', 'drive0') 673 self.assert_qmp(result, 'return[0]/speed', 4 * 1024 * 1024) 674 675 self.cancel_and_wait() 676 677 def test_set_speed_invalid(self): 678 self.assert_no_active_mirrors() 679 680 result = self.vm.qmp('drive-mirror', device='drive0', sync='full', 681 target=target_img, speed=-1) 682 self.assert_qmp(result, 'error/class', 'GenericError') 683 684 self.assert_no_active_mirrors() 685 686 result = self.vm.qmp('drive-mirror', device='drive0', sync='full', 687 target=target_img) 688 self.assert_qmp(result, 'return', {}) 689 690 result = self.vm.qmp('block-job-set-speed', device='drive0', speed=-1) 691 self.assert_qmp(result, 'error/class', 'GenericError') 692 693 self.cancel_and_wait() 694 695if __name__ == '__main__': 696 iotests.main(supported_fmts=['qcow2', 'qed']) 697