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