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