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