1#!/usr/bin/env python 2# 3# Tests for drive-backup and blockdev-backup 4# 5# Copyright (C) 2013, 2014 Red Hat, Inc. 6# 7# Based on 041. 8# 9# This program is free software; you can redistribute it and/or modify 10# it under the terms of the GNU General Public License as published by 11# the Free Software Foundation; either version 2 of the License, or 12# (at your option) any later version. 13# 14# This program is distributed in the hope that it will be useful, 15# but WITHOUT ANY WARRANTY; without even the implied warranty of 16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17# GNU General Public License for more details. 18# 19# You should have received a copy of the GNU General Public License 20# along with this program. If not, see <http://www.gnu.org/licenses/>. 21# 22 23import time 24import os 25import iotests 26from iotests import qemu_img, qemu_io 27 28test_img = os.path.join(iotests.test_dir, 'test.img') 29target_img = os.path.join(iotests.test_dir, 'target.img') 30blockdev_target_img = os.path.join(iotests.test_dir, 'blockdev-target.img') 31 32class TestSingleDrive(iotests.QMPTestCase): 33 image_len = 64 * 1024 * 1024 # MB 34 35 def setUp(self): 36 # Write data to the image so we can compare later 37 qemu_img('create', '-f', iotests.imgfmt, test_img, str(TestSingleDrive.image_len)) 38 qemu_io('-f', iotests.imgfmt, '-c', 'write -P0x5d 0 64k', test_img) 39 qemu_io('-f', iotests.imgfmt, '-c', 'write -P0xd5 1M 32k', test_img) 40 qemu_io('-f', iotests.imgfmt, '-c', 'write -P0xdc 32M 124k', test_img) 41 qemu_io('-f', iotests.imgfmt, '-c', 'write -P0xdc 67043328 64k', test_img) 42 qemu_img('create', '-f', iotests.imgfmt, blockdev_target_img, str(TestSingleDrive.image_len)) 43 44 self.vm = iotests.VM().add_drive(test_img).add_drive(blockdev_target_img) 45 if iotests.qemu_default_machine == 'pc': 46 self.vm.add_drive(None, 'media=cdrom', 'ide') 47 self.vm.launch() 48 49 def tearDown(self): 50 self.vm.shutdown() 51 os.remove(test_img) 52 os.remove(blockdev_target_img) 53 try: 54 os.remove(target_img) 55 except OSError: 56 pass 57 58 def do_test_cancel(self, cmd, target): 59 self.assert_no_active_block_jobs() 60 61 result = self.vm.qmp(cmd, device='drive0', target=target, sync='full') 62 self.assert_qmp(result, 'return', {}) 63 64 event = self.cancel_and_wait() 65 self.assert_qmp(event, 'data/type', 'backup') 66 67 def test_cancel_drive_backup(self): 68 self.do_test_cancel('drive-backup', target_img) 69 70 def test_cancel_blockdev_backup(self): 71 self.do_test_cancel('blockdev-backup', 'drive1') 72 73 def do_test_pause(self, cmd, target, image): 74 self.assert_no_active_block_jobs() 75 76 self.vm.pause_drive('drive0') 77 result = self.vm.qmp(cmd, device='drive0', 78 target=target, sync='full') 79 self.assert_qmp(result, 'return', {}) 80 81 result = self.vm.qmp('block-job-pause', device='drive0') 82 self.assert_qmp(result, 'return', {}) 83 84 self.vm.resume_drive('drive0') 85 time.sleep(1) 86 result = self.vm.qmp('query-block-jobs') 87 offset = self.dictpath(result, 'return[0]/offset') 88 89 time.sleep(1) 90 result = self.vm.qmp('query-block-jobs') 91 self.assert_qmp(result, 'return[0]/offset', offset) 92 93 result = self.vm.qmp('block-job-resume', device='drive0') 94 self.assert_qmp(result, 'return', {}) 95 96 self.wait_until_completed() 97 98 self.vm.shutdown() 99 self.assertTrue(iotests.compare_images(test_img, image), 100 'target image does not match source after backup') 101 102 def test_pause_drive_backup(self): 103 self.do_test_pause('drive-backup', target_img, target_img) 104 105 def test_pause_blockdev_backup(self): 106 self.do_test_pause('blockdev-backup', 'drive1', blockdev_target_img) 107 108 def test_medium_not_found(self): 109 if iotests.qemu_default_machine != 'pc': 110 return 111 112 result = self.vm.qmp('drive-backup', device='drive2', # CD-ROM 113 target=target_img, sync='full') 114 self.assert_qmp(result, 'error/class', 'GenericError') 115 116 def test_medium_not_found_blockdev_backup(self): 117 if iotests.qemu_default_machine != 'pc': 118 return 119 120 result = self.vm.qmp('blockdev-backup', device='drive2', # CD-ROM 121 target='drive1', sync='full') 122 self.assert_qmp(result, 'error/class', 'GenericError') 123 124 def test_image_not_found(self): 125 result = self.vm.qmp('drive-backup', device='drive0', 126 target=target_img, sync='full', mode='existing') 127 self.assert_qmp(result, 'error/class', 'GenericError') 128 129 def test_invalid_format(self): 130 result = self.vm.qmp('drive-backup', device='drive0', 131 target=target_img, sync='full', 132 format='spaghetti-noodles') 133 self.assert_qmp(result, 'error/class', 'GenericError') 134 135 def do_test_device_not_found(self, cmd, **args): 136 result = self.vm.qmp(cmd, **args) 137 self.assert_qmp(result, 'error/class', 'GenericError') 138 139 def test_device_not_found(self): 140 self.do_test_device_not_found('drive-backup', device='nonexistent', 141 target=target_img, sync='full') 142 143 self.do_test_device_not_found('blockdev-backup', device='nonexistent', 144 target='drive0', sync='full') 145 146 self.do_test_device_not_found('blockdev-backup', device='drive0', 147 target='nonexistent', sync='full') 148 149 self.do_test_device_not_found('blockdev-backup', device='nonexistent', 150 target='nonexistent', sync='full') 151 152 def test_target_is_source(self): 153 result = self.vm.qmp('blockdev-backup', device='drive0', 154 target='drive0', sync='full') 155 self.assert_qmp(result, 'error/class', 'GenericError') 156 157class TestSetSpeed(iotests.QMPTestCase): 158 image_len = 80 * 1024 * 1024 # MB 159 160 def setUp(self): 161 qemu_img('create', '-f', iotests.imgfmt, test_img, str(TestSetSpeed.image_len)) 162 qemu_io('-f', iotests.imgfmt, '-c', 'write -P1 0 512', test_img) 163 qemu_img('create', '-f', iotests.imgfmt, blockdev_target_img, str(TestSingleDrive.image_len)) 164 165 self.vm = iotests.VM().add_drive(test_img).add_drive(blockdev_target_img) 166 self.vm.launch() 167 168 def tearDown(self): 169 self.vm.shutdown() 170 os.remove(test_img) 171 os.remove(blockdev_target_img) 172 try: 173 os.remove(target_img) 174 except OSError: 175 pass 176 177 def do_test_set_speed(self, cmd, target): 178 self.assert_no_active_block_jobs() 179 180 self.vm.pause_drive('drive0') 181 result = self.vm.qmp(cmd, device='drive0', target=target, sync='full') 182 self.assert_qmp(result, 'return', {}) 183 184 # Default speed is 0 185 result = self.vm.qmp('query-block-jobs') 186 self.assert_qmp(result, 'return[0]/device', 'drive0') 187 self.assert_qmp(result, 'return[0]/speed', 0) 188 189 result = self.vm.qmp('block-job-set-speed', device='drive0', speed=8 * 1024 * 1024) 190 self.assert_qmp(result, 'return', {}) 191 192 # Ensure the speed we set was accepted 193 result = self.vm.qmp('query-block-jobs') 194 self.assert_qmp(result, 'return[0]/device', 'drive0') 195 self.assert_qmp(result, 'return[0]/speed', 8 * 1024 * 1024) 196 197 event = self.cancel_and_wait(resume=True) 198 self.assert_qmp(event, 'data/type', 'backup') 199 200 # Check setting speed option works 201 self.vm.pause_drive('drive0') 202 result = self.vm.qmp(cmd, device='drive0', 203 target=target, sync='full', speed=4*1024*1024) 204 self.assert_qmp(result, 'return', {}) 205 206 result = self.vm.qmp('query-block-jobs') 207 self.assert_qmp(result, 'return[0]/device', 'drive0') 208 self.assert_qmp(result, 'return[0]/speed', 4 * 1024 * 1024) 209 210 event = self.cancel_and_wait(resume=True) 211 self.assert_qmp(event, 'data/type', 'backup') 212 213 def test_set_speed_drive_backup(self): 214 self.do_test_set_speed('drive-backup', target_img) 215 216 def test_set_speed_blockdev_backup(self): 217 self.do_test_set_speed('blockdev-backup', 'drive1') 218 219 def do_test_set_speed_invalid(self, cmd, target): 220 self.assert_no_active_block_jobs() 221 222 result = self.vm.qmp(cmd, device='drive0', 223 target=target, sync='full', speed=-1) 224 self.assert_qmp(result, 'error/class', 'GenericError') 225 226 self.assert_no_active_block_jobs() 227 228 self.vm.pause_drive('drive0') 229 result = self.vm.qmp(cmd, device='drive0', 230 target=target, sync='full') 231 self.assert_qmp(result, 'return', {}) 232 233 result = self.vm.qmp('block-job-set-speed', device='drive0', speed=-1) 234 self.assert_qmp(result, 'error/class', 'GenericError') 235 236 event = self.cancel_and_wait(resume=True) 237 self.assert_qmp(event, 'data/type', 'backup') 238 239 def test_set_speed_invalid_drive_backup(self): 240 self.do_test_set_speed_invalid('drive-backup', target_img) 241 242 def test_set_speed_invalid_blockdev_backup(self): 243 self.do_test_set_speed_invalid('blockdev-backup', 'drive1') 244 245class TestSingleTransaction(iotests.QMPTestCase): 246 image_len = 64 * 1024 * 1024 # MB 247 248 def setUp(self): 249 qemu_img('create', '-f', iotests.imgfmt, test_img, str(TestSingleTransaction.image_len)) 250 qemu_io('-f', iotests.imgfmt, '-c', 'write -P0x5d 0 64k', test_img) 251 qemu_io('-f', iotests.imgfmt, '-c', 'write -P0xd5 1M 32k', test_img) 252 qemu_io('-f', iotests.imgfmt, '-c', 'write -P0xdc 32M 124k', test_img) 253 qemu_io('-f', iotests.imgfmt, '-c', 'write -P0xdc 67043328 64k', test_img) 254 qemu_img('create', '-f', iotests.imgfmt, blockdev_target_img, str(TestSingleDrive.image_len)) 255 256 self.vm = iotests.VM().add_drive(test_img).add_drive(blockdev_target_img) 257 if iotests.qemu_default_machine == 'pc': 258 self.vm.add_drive(None, 'media=cdrom', 'ide') 259 self.vm.launch() 260 261 def tearDown(self): 262 self.vm.shutdown() 263 os.remove(test_img) 264 os.remove(blockdev_target_img) 265 try: 266 os.remove(target_img) 267 except OSError: 268 pass 269 270 def do_test_cancel(self, cmd, target): 271 self.assert_no_active_block_jobs() 272 273 result = self.vm.qmp('transaction', actions=[{ 274 'type': cmd, 275 'data': { 'device': 'drive0', 276 'target': target, 277 'sync': 'full' }, 278 } 279 ]) 280 281 self.assert_qmp(result, 'return', {}) 282 283 event = self.cancel_and_wait() 284 self.assert_qmp(event, 'data/type', 'backup') 285 286 def test_cancel_drive_backup(self): 287 self.do_test_cancel('drive-backup', target_img) 288 289 def test_cancel_blockdev_backup(self): 290 self.do_test_cancel('blockdev-backup', 'drive1') 291 292 def do_test_pause(self, cmd, target, image): 293 self.assert_no_active_block_jobs() 294 295 self.vm.pause_drive('drive0') 296 result = self.vm.qmp('transaction', actions=[{ 297 'type': cmd, 298 'data': { 'device': 'drive0', 299 'target': target, 300 'sync': 'full' }, 301 } 302 ]) 303 self.assert_qmp(result, 'return', {}) 304 305 result = self.vm.qmp('block-job-pause', device='drive0') 306 self.assert_qmp(result, 'return', {}) 307 308 self.vm.resume_drive('drive0') 309 time.sleep(1) 310 result = self.vm.qmp('query-block-jobs') 311 offset = self.dictpath(result, 'return[0]/offset') 312 313 time.sleep(1) 314 result = self.vm.qmp('query-block-jobs') 315 self.assert_qmp(result, 'return[0]/offset', offset) 316 317 result = self.vm.qmp('block-job-resume', device='drive0') 318 self.assert_qmp(result, 'return', {}) 319 320 self.wait_until_completed() 321 322 self.vm.shutdown() 323 self.assertTrue(iotests.compare_images(test_img, image), 324 'target image does not match source after backup') 325 326 def test_pause_drive_backup(self): 327 self.do_test_pause('drive-backup', target_img, target_img) 328 329 def test_pause_blockdev_backup(self): 330 self.do_test_pause('blockdev-backup', 'drive1', blockdev_target_img) 331 332 def do_test_medium_not_found(self, cmd, target): 333 if iotests.qemu_default_machine != 'pc': 334 return 335 336 result = self.vm.qmp('transaction', actions=[{ 337 'type': cmd, 338 'data': { 'device': 'drive2', # CD-ROM 339 'target': target, 340 'sync': 'full' }, 341 } 342 ]) 343 self.assert_qmp(result, 'error/class', 'GenericError') 344 345 def test_medium_not_found_drive_backup(self): 346 self.do_test_medium_not_found('drive-backup', target_img) 347 348 def test_medium_not_found_blockdev_backup(self): 349 self.do_test_medium_not_found('blockdev-backup', 'drive1') 350 351 def test_image_not_found(self): 352 result = self.vm.qmp('transaction', actions=[{ 353 'type': 'drive-backup', 354 'data': { 'device': 'drive0', 355 'mode': 'existing', 356 'target': target_img, 357 'sync': 'full' }, 358 } 359 ]) 360 self.assert_qmp(result, 'error/class', 'GenericError') 361 362 def test_device_not_found(self): 363 result = self.vm.qmp('transaction', actions=[{ 364 'type': 'drive-backup', 365 'data': { 'device': 'nonexistent', 366 'mode': 'existing', 367 'target': target_img, 368 'sync': 'full' }, 369 } 370 ]) 371 self.assert_qmp(result, 'error/class', 'GenericError') 372 373 result = self.vm.qmp('transaction', actions=[{ 374 'type': 'blockdev-backup', 375 'data': { 'device': 'nonexistent', 376 'target': 'drive1', 377 'sync': 'full' }, 378 } 379 ]) 380 self.assert_qmp(result, 'error/class', 'GenericError') 381 382 result = self.vm.qmp('transaction', actions=[{ 383 'type': 'blockdev-backup', 384 'data': { 'device': 'drive0', 385 'target': 'nonexistent', 386 'sync': 'full' }, 387 } 388 ]) 389 self.assert_qmp(result, 'error/class', 'GenericError') 390 391 result = self.vm.qmp('transaction', actions=[{ 392 'type': 'blockdev-backup', 393 'data': { 'device': 'nonexistent', 394 'target': 'nonexistent', 395 'sync': 'full' }, 396 } 397 ]) 398 self.assert_qmp(result, 'error/class', 'GenericError') 399 400 def test_target_is_source(self): 401 result = self.vm.qmp('transaction', actions=[{ 402 'type': 'blockdev-backup', 403 'data': { 'device': 'drive0', 404 'target': 'drive0', 405 'sync': 'full' }, 406 } 407 ]) 408 self.assert_qmp(result, 'error/class', 'GenericError') 409 410 def test_abort(self): 411 result = self.vm.qmp('transaction', actions=[{ 412 'type': 'drive-backup', 413 'data': { 'device': 'nonexistent', 414 'mode': 'existing', 415 'target': target_img, 416 'sync': 'full' }, 417 }, { 418 'type': 'Abort', 419 'data': {}, 420 } 421 ]) 422 self.assert_qmp(result, 'error/class', 'GenericError') 423 self.assert_no_active_block_jobs() 424 425 result = self.vm.qmp('transaction', actions=[{ 426 'type': 'blockdev-backup', 427 'data': { 'device': 'nonexistent', 428 'target': 'drive1', 429 'sync': 'full' }, 430 }, { 431 'type': 'Abort', 432 'data': {}, 433 } 434 ]) 435 self.assert_qmp(result, 'error/class', 'GenericError') 436 self.assert_no_active_block_jobs() 437 438 result = self.vm.qmp('transaction', actions=[{ 439 'type': 'blockdev-backup', 440 'data': { 'device': 'drive0', 441 'target': 'nonexistent', 442 'sync': 'full' }, 443 }, { 444 'type': 'Abort', 445 'data': {}, 446 } 447 ]) 448 self.assert_qmp(result, 'error/class', 'GenericError') 449 self.assert_no_active_block_jobs() 450 451 452class TestDriveCompression(iotests.QMPTestCase): 453 image_len = 64 * 1024 * 1024 # MB 454 fmt_supports_compression = [{'type': 'qcow2', 'args': ()}, 455 {'type': 'vmdk', 'args': ('-o', 'subformat=streamOptimized')}] 456 457 def setUp(self): 458 # Write data to the image so we can compare later 459 qemu_img('create', '-f', iotests.imgfmt, test_img, str(TestDriveCompression.image_len)) 460 qemu_io('-f', iotests.imgfmt, '-c', 'write -P0x11 0 64k', test_img) 461 qemu_io('-f', iotests.imgfmt, '-c', 'write -P0x00 64k 128k', test_img) 462 qemu_io('-f', iotests.imgfmt, '-c', 'write -P0x22 162k 32k', test_img) 463 qemu_io('-f', iotests.imgfmt, '-c', 'write -P0x33 67043328 64k', test_img) 464 465 def tearDown(self): 466 self.vm.shutdown() 467 os.remove(test_img) 468 os.remove(blockdev_target_img) 469 try: 470 os.remove(target_img) 471 except OSError: 472 pass 473 474 def do_prepare_drives(self, fmt, args): 475 self.vm = iotests.VM().add_drive(test_img) 476 477 qemu_img('create', '-f', fmt, blockdev_target_img, 478 str(TestDriveCompression.image_len), *args) 479 self.vm.add_drive(blockdev_target_img, format=fmt) 480 481 self.vm.launch() 482 483 def do_test_compress_complete(self, cmd, format, **args): 484 self.do_prepare_drives(format['type'], format['args']) 485 486 self.assert_no_active_block_jobs() 487 488 result = self.vm.qmp(cmd, device='drive0', sync='full', compress=True, **args) 489 self.assert_qmp(result, 'return', {}) 490 491 self.wait_until_completed() 492 493 self.vm.shutdown() 494 self.assertTrue(iotests.compare_images(test_img, blockdev_target_img, 495 iotests.imgfmt, format['type']), 496 'target image does not match source after backup') 497 498 def test_complete_compress_drive_backup(self): 499 for format in TestDriveCompression.fmt_supports_compression: 500 self.do_test_compress_complete('drive-backup', format, 501 target=blockdev_target_img, mode='existing') 502 503 def test_complete_compress_blockdev_backup(self): 504 for format in TestDriveCompression.fmt_supports_compression: 505 self.do_test_compress_complete('blockdev-backup', format, target='drive1') 506 507 def do_test_compress_cancel(self, cmd, format, **args): 508 self.do_prepare_drives(format['type'], format['args']) 509 510 self.assert_no_active_block_jobs() 511 512 result = self.vm.qmp(cmd, device='drive0', sync='full', compress=True, **args) 513 self.assert_qmp(result, 'return', {}) 514 515 event = self.cancel_and_wait() 516 self.assert_qmp(event, 'data/type', 'backup') 517 518 self.vm.shutdown() 519 520 def test_compress_cancel_drive_backup(self): 521 for format in TestDriveCompression.fmt_supports_compression: 522 self.do_test_compress_cancel('drive-backup', format, 523 target=blockdev_target_img, mode='existing') 524 525 def test_compress_cancel_blockdev_backup(self): 526 for format in TestDriveCompression.fmt_supports_compression: 527 self.do_test_compress_cancel('blockdev-backup', format, target='drive1') 528 529 def do_test_compress_pause(self, cmd, format, **args): 530 self.do_prepare_drives(format['type'], format['args']) 531 532 self.assert_no_active_block_jobs() 533 534 self.vm.pause_drive('drive0') 535 result = self.vm.qmp(cmd, device='drive0', sync='full', compress=True, **args) 536 self.assert_qmp(result, 'return', {}) 537 538 result = self.vm.qmp('block-job-pause', device='drive0') 539 self.assert_qmp(result, 'return', {}) 540 541 self.vm.resume_drive('drive0') 542 time.sleep(1) 543 result = self.vm.qmp('query-block-jobs') 544 offset = self.dictpath(result, 'return[0]/offset') 545 546 time.sleep(1) 547 result = self.vm.qmp('query-block-jobs') 548 self.assert_qmp(result, 'return[0]/offset', offset) 549 550 result = self.vm.qmp('block-job-resume', device='drive0') 551 self.assert_qmp(result, 'return', {}) 552 553 self.wait_until_completed() 554 555 self.vm.shutdown() 556 self.assertTrue(iotests.compare_images(test_img, blockdev_target_img, 557 iotests.imgfmt, format['type']), 558 'target image does not match source after backup') 559 560 def test_compress_pause_drive_backup(self): 561 for format in TestDriveCompression.fmt_supports_compression: 562 self.do_test_compress_pause('drive-backup', format, 563 target=blockdev_target_img, mode='existing') 564 565 def test_compress_pause_blockdev_backup(self): 566 for format in TestDriveCompression.fmt_supports_compression: 567 self.do_test_compress_pause('blockdev-backup', format, target='drive1') 568 569if __name__ == '__main__': 570 iotests.main(supported_fmts=['raw', 'qcow2']) 571