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 self.vm.launch() 46 47 def tearDown(self): 48 self.vm.shutdown() 49 os.remove(test_img) 50 os.remove(blockdev_target_img) 51 try: 52 os.remove(target_img) 53 except OSError: 54 pass 55 56 def do_test_cancel(self, cmd, target): 57 self.assert_no_active_block_jobs() 58 59 result = self.vm.qmp(cmd, device='drive0', target=target, sync='full') 60 self.assert_qmp(result, 'return', {}) 61 62 event = self.cancel_and_wait() 63 self.assert_qmp(event, 'data/type', 'backup') 64 65 def test_cancel_drive_backup(self): 66 self.do_test_cancel('drive-backup', target_img) 67 68 def test_cancel_blockdev_backup(self): 69 self.do_test_cancel('blockdev-backup', 'drive1') 70 71 def do_test_pause(self, cmd, target, image): 72 self.assert_no_active_block_jobs() 73 74 self.vm.pause_drive('drive0') 75 result = self.vm.qmp(cmd, device='drive0', 76 target=target, sync='full') 77 self.assert_qmp(result, 'return', {}) 78 79 result = self.vm.qmp('block-job-pause', device='drive0') 80 self.assert_qmp(result, 'return', {}) 81 82 self.vm.resume_drive('drive0') 83 time.sleep(1) 84 result = self.vm.qmp('query-block-jobs') 85 offset = self.dictpath(result, 'return[0]/offset') 86 87 time.sleep(1) 88 result = self.vm.qmp('query-block-jobs') 89 self.assert_qmp(result, 'return[0]/offset', offset) 90 91 result = self.vm.qmp('block-job-resume', device='drive0') 92 self.assert_qmp(result, 'return', {}) 93 94 self.wait_until_completed() 95 96 self.vm.shutdown() 97 self.assertTrue(iotests.compare_images(test_img, image), 98 'target image does not match source after backup') 99 100 def test_pause_drive_backup(self): 101 self.do_test_pause('drive-backup', target_img, target_img) 102 103 def test_pause_blockdev_backup(self): 104 self.do_test_pause('blockdev-backup', 'drive1', blockdev_target_img) 105 106 def test_medium_not_found(self): 107 result = self.vm.qmp('drive-backup', device='ide1-cd0', 108 target=target_img, sync='full') 109 self.assert_qmp(result, 'error/class', 'GenericError') 110 111 def test_medium_not_found_blockdev_backup(self): 112 result = self.vm.qmp('blockdev-backup', device='ide1-cd0', 113 target='drive1', sync='full') 114 self.assert_qmp(result, 'error/class', 'GenericError') 115 116 def test_image_not_found(self): 117 result = self.vm.qmp('drive-backup', device='drive0', 118 target=target_img, sync='full', mode='existing') 119 self.assert_qmp(result, 'error/class', 'GenericError') 120 121 def test_invalid_format(self): 122 result = self.vm.qmp('drive-backup', device='drive0', 123 target=target_img, sync='full', 124 format='spaghetti-noodles') 125 self.assert_qmp(result, 'error/class', 'GenericError') 126 127 def do_test_device_not_found(self, cmd, **args): 128 result = self.vm.qmp(cmd, **args) 129 if cmd == 'drive-backup': 130 self.assert_qmp(result, 'error/class', 'DeviceNotFound') 131 else: 132 self.assert_qmp(result, 'error/class', 'GenericError') 133 134 def test_device_not_found(self): 135 self.do_test_device_not_found('drive-backup', device='nonexistent', 136 target=target_img, sync='full') 137 138 self.do_test_device_not_found('blockdev-backup', device='nonexistent', 139 target='drive0', sync='full') 140 141 self.do_test_device_not_found('blockdev-backup', device='drive0', 142 target='nonexistent', sync='full') 143 144 self.do_test_device_not_found('blockdev-backup', device='nonexistent', 145 target='nonexistent', sync='full') 146 147 def test_target_is_source(self): 148 result = self.vm.qmp('blockdev-backup', device='drive0', 149 target='drive0', sync='full') 150 self.assert_qmp(result, 'error/class', 'GenericError') 151 152class TestSetSpeed(iotests.QMPTestCase): 153 image_len = 80 * 1024 * 1024 # MB 154 155 def setUp(self): 156 qemu_img('create', '-f', iotests.imgfmt, test_img, str(TestSetSpeed.image_len)) 157 qemu_io('-f', iotests.imgfmt, '-c', 'write -P1 0 512', test_img) 158 qemu_img('create', '-f', iotests.imgfmt, blockdev_target_img, str(TestSingleDrive.image_len)) 159 160 self.vm = iotests.VM().add_drive(test_img).add_drive(blockdev_target_img) 161 self.vm.launch() 162 163 def tearDown(self): 164 self.vm.shutdown() 165 os.remove(test_img) 166 os.remove(blockdev_target_img) 167 try: 168 os.remove(target_img) 169 except OSError: 170 pass 171 172 def do_test_set_speed(self, cmd, target): 173 self.assert_no_active_block_jobs() 174 175 self.vm.pause_drive('drive0') 176 result = self.vm.qmp(cmd, device='drive0', target=target, sync='full') 177 self.assert_qmp(result, 'return', {}) 178 179 # Default speed is 0 180 result = self.vm.qmp('query-block-jobs') 181 self.assert_qmp(result, 'return[0]/device', 'drive0') 182 self.assert_qmp(result, 'return[0]/speed', 0) 183 184 result = self.vm.qmp('block-job-set-speed', device='drive0', speed=8 * 1024 * 1024) 185 self.assert_qmp(result, 'return', {}) 186 187 # Ensure the speed we set was accepted 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', 8 * 1024 * 1024) 191 192 event = self.cancel_and_wait(resume=True) 193 self.assert_qmp(event, 'data/type', 'backup') 194 195 # Check setting speed option works 196 self.vm.pause_drive('drive0') 197 result = self.vm.qmp(cmd, device='drive0', 198 target=target, sync='full', speed=4*1024*1024) 199 self.assert_qmp(result, 'return', {}) 200 201 result = self.vm.qmp('query-block-jobs') 202 self.assert_qmp(result, 'return[0]/device', 'drive0') 203 self.assert_qmp(result, 'return[0]/speed', 4 * 1024 * 1024) 204 205 event = self.cancel_and_wait(resume=True) 206 self.assert_qmp(event, 'data/type', 'backup') 207 208 def test_set_speed_drive_backup(self): 209 self.do_test_set_speed('drive-backup', target_img) 210 211 def test_set_speed_blockdev_backup(self): 212 self.do_test_set_speed('blockdev-backup', 'drive1') 213 214 def do_test_set_speed_invalid(self, cmd, target): 215 self.assert_no_active_block_jobs() 216 217 result = self.vm.qmp(cmd, device='drive0', 218 target=target, sync='full', speed=-1) 219 self.assert_qmp(result, 'error/class', 'GenericError') 220 221 self.assert_no_active_block_jobs() 222 223 self.vm.pause_drive('drive0') 224 result = self.vm.qmp(cmd, device='drive0', 225 target=target, sync='full') 226 self.assert_qmp(result, 'return', {}) 227 228 result = self.vm.qmp('block-job-set-speed', device='drive0', speed=-1) 229 self.assert_qmp(result, 'error/class', 'GenericError') 230 231 event = self.cancel_and_wait(resume=True) 232 self.assert_qmp(event, 'data/type', 'backup') 233 234 def test_set_speed_invalid_drive_backup(self): 235 self.do_test_set_speed_invalid('drive-backup', target_img) 236 237 def test_set_speed_invalid_blockdev_backup(self): 238 self.do_test_set_speed_invalid('blockdev-backup', 'drive1') 239 240class TestSingleTransaction(iotests.QMPTestCase): 241 image_len = 64 * 1024 * 1024 # MB 242 243 def setUp(self): 244 qemu_img('create', '-f', iotests.imgfmt, test_img, str(TestSingleTransaction.image_len)) 245 qemu_io('-f', iotests.imgfmt, '-c', 'write -P0x5d 0 64k', test_img) 246 qemu_io('-f', iotests.imgfmt, '-c', 'write -P0xd5 1M 32k', test_img) 247 qemu_io('-f', iotests.imgfmt, '-c', 'write -P0xdc 32M 124k', test_img) 248 qemu_io('-f', iotests.imgfmt, '-c', 'write -P0xdc 67043328 64k', test_img) 249 qemu_img('create', '-f', iotests.imgfmt, blockdev_target_img, str(TestSingleDrive.image_len)) 250 251 self.vm = iotests.VM().add_drive(test_img).add_drive(blockdev_target_img) 252 self.vm.launch() 253 254 def tearDown(self): 255 self.vm.shutdown() 256 os.remove(test_img) 257 os.remove(blockdev_target_img) 258 try: 259 os.remove(target_img) 260 except OSError: 261 pass 262 263 def do_test_cancel(self, cmd, target): 264 self.assert_no_active_block_jobs() 265 266 result = self.vm.qmp('transaction', actions=[{ 267 'type': cmd, 268 'data': { 'device': 'drive0', 269 'target': target, 270 'sync': 'full' }, 271 } 272 ]) 273 274 self.assert_qmp(result, 'return', {}) 275 276 event = self.cancel_and_wait() 277 self.assert_qmp(event, 'data/type', 'backup') 278 279 def test_cancel_drive_backup(self): 280 self.do_test_cancel('drive-backup', target_img) 281 282 def test_cancel_blockdev_backup(self): 283 self.do_test_cancel('blockdev-backup', 'drive1') 284 285 def do_test_pause(self, cmd, target, image): 286 self.assert_no_active_block_jobs() 287 288 self.vm.pause_drive('drive0') 289 result = self.vm.qmp('transaction', actions=[{ 290 'type': cmd, 291 'data': { 'device': 'drive0', 292 'target': target, 293 'sync': 'full' }, 294 } 295 ]) 296 self.assert_qmp(result, 'return', {}) 297 298 result = self.vm.qmp('block-job-pause', device='drive0') 299 self.assert_qmp(result, 'return', {}) 300 301 self.vm.resume_drive('drive0') 302 time.sleep(1) 303 result = self.vm.qmp('query-block-jobs') 304 offset = self.dictpath(result, 'return[0]/offset') 305 306 time.sleep(1) 307 result = self.vm.qmp('query-block-jobs') 308 self.assert_qmp(result, 'return[0]/offset', offset) 309 310 result = self.vm.qmp('block-job-resume', device='drive0') 311 self.assert_qmp(result, 'return', {}) 312 313 self.wait_until_completed() 314 315 self.vm.shutdown() 316 self.assertTrue(iotests.compare_images(test_img, image), 317 'target image does not match source after backup') 318 319 def test_pause_drive_backup(self): 320 self.do_test_pause('drive-backup', target_img, target_img) 321 322 def test_pause_blockdev_backup(self): 323 self.do_test_pause('blockdev-backup', 'drive1', blockdev_target_img) 324 325 def do_test_medium_not_found(self, cmd, target): 326 result = self.vm.qmp('transaction', actions=[{ 327 'type': cmd, 328 'data': { 'device': 'ide1-cd0', 329 'target': target, 330 'sync': 'full' }, 331 } 332 ]) 333 self.assert_qmp(result, 'error/class', 'GenericError') 334 335 def test_medium_not_found_drive_backup(self): 336 self.do_test_medium_not_found('drive-backup', target_img) 337 338 def test_medium_not_found_blockdev_backup(self): 339 self.do_test_medium_not_found('blockdev-backup', 'drive1') 340 341 def test_image_not_found(self): 342 result = self.vm.qmp('transaction', actions=[{ 343 'type': 'drive-backup', 344 'data': { 'device': 'drive0', 345 'mode': 'existing', 346 'target': target_img, 347 'sync': 'full' }, 348 } 349 ]) 350 self.assert_qmp(result, 'error/class', 'GenericError') 351 352 def test_device_not_found(self): 353 result = self.vm.qmp('transaction', actions=[{ 354 'type': 'drive-backup', 355 'data': { 'device': 'nonexistent', 356 'mode': 'existing', 357 'target': target_img, 358 'sync': 'full' }, 359 } 360 ]) 361 self.assert_qmp(result, 'error/class', 'DeviceNotFound') 362 363 result = self.vm.qmp('transaction', actions=[{ 364 'type': 'blockdev-backup', 365 'data': { 'device': 'nonexistent', 366 'target': 'drive1', 367 'sync': 'full' }, 368 } 369 ]) 370 self.assert_qmp(result, 'error/class', 'GenericError') 371 372 result = self.vm.qmp('transaction', actions=[{ 373 'type': 'blockdev-backup', 374 'data': { 'device': 'drive0', 375 'target': 'nonexistent', 376 'sync': 'full' }, 377 } 378 ]) 379 self.assert_qmp(result, 'error/class', 'GenericError') 380 381 result = self.vm.qmp('transaction', actions=[{ 382 'type': 'blockdev-backup', 383 'data': { 'device': 'nonexistent', 384 'target': 'nonexistent', 385 'sync': 'full' }, 386 } 387 ]) 388 self.assert_qmp(result, 'error/class', 'GenericError') 389 390 def test_target_is_source(self): 391 result = self.vm.qmp('transaction', actions=[{ 392 'type': 'blockdev-backup', 393 'data': { 'device': 'drive0', 394 'target': 'drive0', 395 'sync': 'full' }, 396 } 397 ]) 398 self.assert_qmp(result, 'error/class', 'GenericError') 399 400 def test_abort(self): 401 result = self.vm.qmp('transaction', actions=[{ 402 'type': 'drive-backup', 403 'data': { 'device': 'nonexistent', 404 'mode': 'existing', 405 'target': target_img, 406 'sync': 'full' }, 407 }, { 408 'type': 'Abort', 409 'data': {}, 410 } 411 ]) 412 self.assert_qmp(result, 'error/class', 'GenericError') 413 self.assert_no_active_block_jobs() 414 415 result = self.vm.qmp('transaction', actions=[{ 416 'type': 'blockdev-backup', 417 'data': { 'device': 'nonexistent', 418 'target': 'drive1', 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': 'drive0', 431 'target': 'nonexistent', 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 441if __name__ == '__main__': 442 iotests.main(supported_fmts=['raw', 'qcow2']) 443