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