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 if cmd == 'drive-backup': 138 self.assert_qmp(result, 'error/class', 'DeviceNotFound') 139 else: 140 self.assert_qmp(result, 'error/class', 'GenericError') 141 142 def test_device_not_found(self): 143 self.do_test_device_not_found('drive-backup', device='nonexistent', 144 target=target_img, sync='full') 145 146 self.do_test_device_not_found('blockdev-backup', device='nonexistent', 147 target='drive0', sync='full') 148 149 self.do_test_device_not_found('blockdev-backup', device='drive0', 150 target='nonexistent', sync='full') 151 152 self.do_test_device_not_found('blockdev-backup', device='nonexistent', 153 target='nonexistent', sync='full') 154 155 def test_target_is_source(self): 156 result = self.vm.qmp('blockdev-backup', device='drive0', 157 target='drive0', sync='full') 158 self.assert_qmp(result, 'error/class', 'GenericError') 159 160class TestSetSpeed(iotests.QMPTestCase): 161 image_len = 80 * 1024 * 1024 # MB 162 163 def setUp(self): 164 qemu_img('create', '-f', iotests.imgfmt, test_img, str(TestSetSpeed.image_len)) 165 qemu_io('-f', iotests.imgfmt, '-c', 'write -P1 0 512', test_img) 166 qemu_img('create', '-f', iotests.imgfmt, blockdev_target_img, str(TestSingleDrive.image_len)) 167 168 self.vm = iotests.VM().add_drive(test_img).add_drive(blockdev_target_img) 169 self.vm.launch() 170 171 def tearDown(self): 172 self.vm.shutdown() 173 os.remove(test_img) 174 os.remove(blockdev_target_img) 175 try: 176 os.remove(target_img) 177 except OSError: 178 pass 179 180 def do_test_set_speed(self, cmd, target): 181 self.assert_no_active_block_jobs() 182 183 self.vm.pause_drive('drive0') 184 result = self.vm.qmp(cmd, device='drive0', target=target, sync='full') 185 self.assert_qmp(result, 'return', {}) 186 187 # Default speed is 0 188 result = self.vm.qmp('query-block-jobs') 189 self.assert_qmp(result, 'return[0]/device', 'drive0') 190 self.assert_qmp(result, 'return[0]/speed', 0) 191 192 result = self.vm.qmp('block-job-set-speed', device='drive0', speed=8 * 1024 * 1024) 193 self.assert_qmp(result, 'return', {}) 194 195 # Ensure the speed we set was accepted 196 result = self.vm.qmp('query-block-jobs') 197 self.assert_qmp(result, 'return[0]/device', 'drive0') 198 self.assert_qmp(result, 'return[0]/speed', 8 * 1024 * 1024) 199 200 event = self.cancel_and_wait(resume=True) 201 self.assert_qmp(event, 'data/type', 'backup') 202 203 # Check setting speed option works 204 self.vm.pause_drive('drive0') 205 result = self.vm.qmp(cmd, device='drive0', 206 target=target, sync='full', speed=4*1024*1024) 207 self.assert_qmp(result, 'return', {}) 208 209 result = self.vm.qmp('query-block-jobs') 210 self.assert_qmp(result, 'return[0]/device', 'drive0') 211 self.assert_qmp(result, 'return[0]/speed', 4 * 1024 * 1024) 212 213 event = self.cancel_and_wait(resume=True) 214 self.assert_qmp(event, 'data/type', 'backup') 215 216 def test_set_speed_drive_backup(self): 217 self.do_test_set_speed('drive-backup', target_img) 218 219 def test_set_speed_blockdev_backup(self): 220 self.do_test_set_speed('blockdev-backup', 'drive1') 221 222 def do_test_set_speed_invalid(self, cmd, target): 223 self.assert_no_active_block_jobs() 224 225 result = self.vm.qmp(cmd, device='drive0', 226 target=target, sync='full', speed=-1) 227 self.assert_qmp(result, 'error/class', 'GenericError') 228 229 self.assert_no_active_block_jobs() 230 231 self.vm.pause_drive('drive0') 232 result = self.vm.qmp(cmd, device='drive0', 233 target=target, sync='full') 234 self.assert_qmp(result, 'return', {}) 235 236 result = self.vm.qmp('block-job-set-speed', device='drive0', speed=-1) 237 self.assert_qmp(result, 'error/class', 'GenericError') 238 239 event = self.cancel_and_wait(resume=True) 240 self.assert_qmp(event, 'data/type', 'backup') 241 242 def test_set_speed_invalid_drive_backup(self): 243 self.do_test_set_speed_invalid('drive-backup', target_img) 244 245 def test_set_speed_invalid_blockdev_backup(self): 246 self.do_test_set_speed_invalid('blockdev-backup', 'drive1') 247 248class TestSingleTransaction(iotests.QMPTestCase): 249 image_len = 64 * 1024 * 1024 # MB 250 251 def setUp(self): 252 qemu_img('create', '-f', iotests.imgfmt, test_img, str(TestSingleTransaction.image_len)) 253 qemu_io('-f', iotests.imgfmt, '-c', 'write -P0x5d 0 64k', test_img) 254 qemu_io('-f', iotests.imgfmt, '-c', 'write -P0xd5 1M 32k', test_img) 255 qemu_io('-f', iotests.imgfmt, '-c', 'write -P0xdc 32M 124k', test_img) 256 qemu_io('-f', iotests.imgfmt, '-c', 'write -P0xdc 67043328 64k', test_img) 257 qemu_img('create', '-f', iotests.imgfmt, blockdev_target_img, str(TestSingleDrive.image_len)) 258 259 self.vm = iotests.VM().add_drive(test_img).add_drive(blockdev_target_img) 260 if iotests.qemu_default_machine == 'pc': 261 self.vm.add_drive(None, 'media=cdrom', 'ide') 262 self.vm.launch() 263 264 def tearDown(self): 265 self.vm.shutdown() 266 os.remove(test_img) 267 os.remove(blockdev_target_img) 268 try: 269 os.remove(target_img) 270 except OSError: 271 pass 272 273 def do_test_cancel(self, cmd, target): 274 self.assert_no_active_block_jobs() 275 276 result = self.vm.qmp('transaction', actions=[{ 277 'type': cmd, 278 'data': { 'device': 'drive0', 279 'target': target, 280 'sync': 'full' }, 281 } 282 ]) 283 284 self.assert_qmp(result, 'return', {}) 285 286 event = self.cancel_and_wait() 287 self.assert_qmp(event, 'data/type', 'backup') 288 289 def test_cancel_drive_backup(self): 290 self.do_test_cancel('drive-backup', target_img) 291 292 def test_cancel_blockdev_backup(self): 293 self.do_test_cancel('blockdev-backup', 'drive1') 294 295 def do_test_pause(self, cmd, target, image): 296 self.assert_no_active_block_jobs() 297 298 self.vm.pause_drive('drive0') 299 result = self.vm.qmp('transaction', actions=[{ 300 'type': cmd, 301 'data': { 'device': 'drive0', 302 'target': target, 303 'sync': 'full' }, 304 } 305 ]) 306 self.assert_qmp(result, 'return', {}) 307 308 result = self.vm.qmp('block-job-pause', device='drive0') 309 self.assert_qmp(result, 'return', {}) 310 311 self.vm.resume_drive('drive0') 312 time.sleep(1) 313 result = self.vm.qmp('query-block-jobs') 314 offset = self.dictpath(result, 'return[0]/offset') 315 316 time.sleep(1) 317 result = self.vm.qmp('query-block-jobs') 318 self.assert_qmp(result, 'return[0]/offset', offset) 319 320 result = self.vm.qmp('block-job-resume', device='drive0') 321 self.assert_qmp(result, 'return', {}) 322 323 self.wait_until_completed() 324 325 self.vm.shutdown() 326 self.assertTrue(iotests.compare_images(test_img, image), 327 'target image does not match source after backup') 328 329 def test_pause_drive_backup(self): 330 self.do_test_pause('drive-backup', target_img, target_img) 331 332 def test_pause_blockdev_backup(self): 333 self.do_test_pause('blockdev-backup', 'drive1', blockdev_target_img) 334 335 def do_test_medium_not_found(self, cmd, target): 336 if iotests.qemu_default_machine != 'pc': 337 return 338 339 result = self.vm.qmp('transaction', actions=[{ 340 'type': cmd, 341 'data': { 'device': 'drive2', # CD-ROM 342 'target': target, 343 'sync': 'full' }, 344 } 345 ]) 346 self.assert_qmp(result, 'error/class', 'GenericError') 347 348 def test_medium_not_found_drive_backup(self): 349 self.do_test_medium_not_found('drive-backup', target_img) 350 351 def test_medium_not_found_blockdev_backup(self): 352 self.do_test_medium_not_found('blockdev-backup', 'drive1') 353 354 def test_image_not_found(self): 355 result = self.vm.qmp('transaction', actions=[{ 356 'type': 'drive-backup', 357 'data': { 'device': 'drive0', 358 'mode': 'existing', 359 'target': target_img, 360 'sync': 'full' }, 361 } 362 ]) 363 self.assert_qmp(result, 'error/class', 'GenericError') 364 365 def test_device_not_found(self): 366 result = self.vm.qmp('transaction', actions=[{ 367 'type': 'drive-backup', 368 'data': { 'device': 'nonexistent', 369 'mode': 'existing', 370 'target': target_img, 371 'sync': 'full' }, 372 } 373 ]) 374 self.assert_qmp(result, 'error/class', 'DeviceNotFound') 375 376 result = self.vm.qmp('transaction', actions=[{ 377 'type': 'blockdev-backup', 378 'data': { 'device': 'nonexistent', 379 'target': 'drive1', 380 'sync': 'full' }, 381 } 382 ]) 383 self.assert_qmp(result, 'error/class', 'GenericError') 384 385 result = self.vm.qmp('transaction', actions=[{ 386 'type': 'blockdev-backup', 387 'data': { 'device': 'drive0', 388 'target': 'nonexistent', 389 'sync': 'full' }, 390 } 391 ]) 392 self.assert_qmp(result, 'error/class', 'GenericError') 393 394 result = self.vm.qmp('transaction', actions=[{ 395 'type': 'blockdev-backup', 396 'data': { 'device': 'nonexistent', 397 'target': 'nonexistent', 398 'sync': 'full' }, 399 } 400 ]) 401 self.assert_qmp(result, 'error/class', 'GenericError') 402 403 def test_target_is_source(self): 404 result = self.vm.qmp('transaction', actions=[{ 405 'type': 'blockdev-backup', 406 'data': { 'device': 'drive0', 407 'target': 'drive0', 408 'sync': 'full' }, 409 } 410 ]) 411 self.assert_qmp(result, 'error/class', 'GenericError') 412 413 def test_abort(self): 414 result = self.vm.qmp('transaction', actions=[{ 415 'type': 'drive-backup', 416 'data': { 'device': 'nonexistent', 417 'mode': 'existing', 418 'target': target_img, 419 'sync': 'full' }, 420 }, { 421 'type': 'Abort', 422 'data': {}, 423 } 424 ]) 425 self.assert_qmp(result, 'error/class', 'GenericError') 426 self.assert_no_active_block_jobs() 427 428 result = self.vm.qmp('transaction', actions=[{ 429 'type': 'blockdev-backup', 430 'data': { 'device': 'nonexistent', 431 'target': 'drive1', 432 'sync': 'full' }, 433 }, { 434 'type': 'Abort', 435 'data': {}, 436 } 437 ]) 438 self.assert_qmp(result, 'error/class', 'GenericError') 439 self.assert_no_active_block_jobs() 440 441 result = self.vm.qmp('transaction', actions=[{ 442 'type': 'blockdev-backup', 443 'data': { 'device': 'drive0', 444 'target': 'nonexistent', 445 'sync': 'full' }, 446 }, { 447 'type': 'Abort', 448 'data': {}, 449 } 450 ]) 451 self.assert_qmp(result, 'error/class', 'GenericError') 452 self.assert_no_active_block_jobs() 453 454if __name__ == '__main__': 455 iotests.main(supported_fmts=['raw', 'qcow2']) 456