 bcdce5a73c
			
		
	
	
		bcdce5a73c
		
	
	
	
	
		
			
			The 'block-commit' command needs the overlay image of 'top' to be opened in read-write mode in order to update the backing file string. If 'top' is not the active layer or its backing file then its overlay needs to be reopened during the block job. This is a test case for that scenario. Signed-off-by: Alberto Garcia <berto@igalia.com> Reviewed-by: Max Reitz <mreitz@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
		
			
				
	
	
		
			286 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			286 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
| #!/usr/bin/env python
 | |
| #
 | |
| # Tests for image block commit.
 | |
| #
 | |
| # Copyright (C) 2012 IBM, Corp.
 | |
| # Copyright (C) 2012 Red Hat, Inc.
 | |
| #
 | |
| # This program is free software; you can redistribute it and/or modify
 | |
| # it under the terms of the GNU General Public License as published by
 | |
| # the Free Software Foundation; either version 2 of the License, or
 | |
| # (at your option) any later version.
 | |
| #
 | |
| # This program is distributed in the hope that it will be useful,
 | |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
| # GNU General Public License for more details.
 | |
| #
 | |
| # You should have received a copy of the GNU General Public License
 | |
| # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | |
| #
 | |
| # Test for live block commit
 | |
| # Derived from Image Streaming Test 030
 | |
| 
 | |
| import time
 | |
| import os
 | |
| import iotests
 | |
| from iotests import qemu_img, qemu_io
 | |
| import struct
 | |
| import errno
 | |
| 
 | |
| backing_img = os.path.join(iotests.test_dir, 'backing.img')
 | |
| mid_img = os.path.join(iotests.test_dir, 'mid.img')
 | |
| test_img = os.path.join(iotests.test_dir, 'test.img')
 | |
| 
 | |
| class ImageCommitTestCase(iotests.QMPTestCase):
 | |
|     '''Abstract base class for image commit test cases'''
 | |
| 
 | |
|     def wait_for_complete(self, need_ready=False):
 | |
|         completed = False
 | |
|         ready = False
 | |
|         while not completed:
 | |
|             for event in self.vm.get_qmp_events(wait=True):
 | |
|                 if event['event'] == 'BLOCK_JOB_COMPLETED':
 | |
|                     self.assert_qmp_absent(event, 'data/error')
 | |
|                     self.assert_qmp(event, 'data/type', 'commit')
 | |
|                     self.assert_qmp(event, 'data/device', 'drive0')
 | |
|                     self.assert_qmp(event, 'data/offset', event['data']['len'])
 | |
|                     if need_ready:
 | |
|                         self.assertTrue(ready, "Expecting BLOCK_JOB_COMPLETED event")
 | |
|                     completed = True
 | |
|                 elif event['event'] == 'BLOCK_JOB_READY':
 | |
|                     ready = True
 | |
|                     self.assert_qmp(event, 'data/type', 'commit')
 | |
|                     self.assert_qmp(event, 'data/device', 'drive0')
 | |
|                     self.vm.qmp('block-job-complete', device='drive0')
 | |
| 
 | |
|         self.assert_no_active_block_jobs()
 | |
|         self.vm.shutdown()
 | |
| 
 | |
|     def run_commit_test(self, top, base, need_ready=False):
 | |
|         self.assert_no_active_block_jobs()
 | |
|         result = self.vm.qmp('block-commit', device='drive0', top=top, base=base)
 | |
|         self.assert_qmp(result, 'return', {})
 | |
|         self.wait_for_complete(need_ready)
 | |
| 
 | |
|     def run_default_commit_test(self):
 | |
|         self.assert_no_active_block_jobs()
 | |
|         result = self.vm.qmp('block-commit', device='drive0')
 | |
|         self.assert_qmp(result, 'return', {})
 | |
|         self.wait_for_complete()
 | |
| 
 | |
| class TestSingleDrive(ImageCommitTestCase):
 | |
|     image_len = 1 * 1024 * 1024
 | |
|     test_len = 1 * 1024 * 256
 | |
| 
 | |
|     def setUp(self):
 | |
|         iotests.create_image(backing_img, self.image_len)
 | |
|         qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, mid_img)
 | |
|         qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % mid_img, test_img)
 | |
|         qemu_io('-f', 'raw', '-c', 'write -P 0xab 0 524288', backing_img)
 | |
|         qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xef 524288 524288', mid_img)
 | |
|         self.vm = iotests.VM().add_drive(test_img)
 | |
|         self.vm.launch()
 | |
| 
 | |
|     def tearDown(self):
 | |
|         self.vm.shutdown()
 | |
|         os.remove(test_img)
 | |
|         os.remove(mid_img)
 | |
|         os.remove(backing_img)
 | |
| 
 | |
|     def test_commit(self):
 | |
|         self.run_commit_test(mid_img, backing_img)
 | |
|         self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', backing_img).find("verification failed"))
 | |
|         self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', backing_img).find("verification failed"))
 | |
| 
 | |
|     def test_device_not_found(self):
 | |
|         result = self.vm.qmp('block-commit', device='nonexistent', top='%s' % mid_img)
 | |
|         self.assert_qmp(result, 'error/class', 'DeviceNotFound')
 | |
| 
 | |
|     def test_top_same_base(self):
 | |
|         self.assert_no_active_block_jobs()
 | |
|         result = self.vm.qmp('block-commit', device='drive0', top='%s' % backing_img, base='%s' % backing_img)
 | |
|         self.assert_qmp(result, 'error/class', 'GenericError')
 | |
|         self.assert_qmp(result, 'error/desc', 'Base \'%s\' not found' % backing_img)
 | |
| 
 | |
|     def test_top_invalid(self):
 | |
|         self.assert_no_active_block_jobs()
 | |
|         result = self.vm.qmp('block-commit', device='drive0', top='badfile', base='%s' % backing_img)
 | |
|         self.assert_qmp(result, 'error/class', 'GenericError')
 | |
|         self.assert_qmp(result, 'error/desc', 'Top image file badfile not found')
 | |
| 
 | |
|     def test_base_invalid(self):
 | |
|         self.assert_no_active_block_jobs()
 | |
|         result = self.vm.qmp('block-commit', device='drive0', top='%s' % mid_img, base='badfile')
 | |
|         self.assert_qmp(result, 'error/class', 'GenericError')
 | |
|         self.assert_qmp(result, 'error/desc', 'Base \'badfile\' not found')
 | |
| 
 | |
|     def test_top_is_active(self):
 | |
|         self.run_commit_test(test_img, backing_img, need_ready=True)
 | |
|         self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', backing_img).find("verification failed"))
 | |
|         self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', backing_img).find("verification failed"))
 | |
| 
 | |
|     def test_top_is_default_active(self):
 | |
|         self.run_default_commit_test()
 | |
|         self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', backing_img).find("verification failed"))
 | |
|         self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', backing_img).find("verification failed"))
 | |
| 
 | |
|     def test_top_and_base_reversed(self):
 | |
|         self.assert_no_active_block_jobs()
 | |
|         result = self.vm.qmp('block-commit', device='drive0', top='%s' % backing_img, base='%s' % mid_img)
 | |
|         self.assert_qmp(result, 'error/class', 'GenericError')
 | |
|         self.assert_qmp(result, 'error/desc', 'Base \'%s\' not found' % mid_img)
 | |
| 
 | |
| 
 | |
| class TestRelativePaths(ImageCommitTestCase):
 | |
|     image_len = 1 * 1024 * 1024
 | |
|     test_len = 1 * 1024 * 256
 | |
| 
 | |
|     dir1 = "dir1"
 | |
|     dir2 = "dir2/"
 | |
|     dir3 = "dir2/dir3/"
 | |
| 
 | |
|     test_img = os.path.join(iotests.test_dir, dir3, 'test.img')
 | |
|     mid_img = "../mid.img"
 | |
|     backing_img = "../dir1/backing.img"
 | |
| 
 | |
|     backing_img_abs = os.path.join(iotests.test_dir, dir1, 'backing.img')
 | |
|     mid_img_abs = os.path.join(iotests.test_dir, dir2, 'mid.img')
 | |
| 
 | |
|     def setUp(self):
 | |
|         try:
 | |
|             os.mkdir(os.path.join(iotests.test_dir, self.dir1))
 | |
|             os.mkdir(os.path.join(iotests.test_dir, self.dir2))
 | |
|             os.mkdir(os.path.join(iotests.test_dir, self.dir3))
 | |
|         except OSError as exception:
 | |
|             if exception.errno != errno.EEXIST:
 | |
|                 raise
 | |
|         iotests.create_image(self.backing_img_abs, TestRelativePaths.image_len)
 | |
|         qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % self.backing_img_abs, self.mid_img_abs)
 | |
|         qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % self.mid_img_abs, self.test_img)
 | |
|         qemu_img('rebase', '-u', '-b', self.backing_img, self.mid_img_abs)
 | |
|         qemu_img('rebase', '-u', '-b', self.mid_img, self.test_img)
 | |
|         qemu_io('-f', 'raw', '-c', 'write -P 0xab 0 524288', self.backing_img_abs)
 | |
|         qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xef 524288 524288', self.mid_img_abs)
 | |
|         self.vm = iotests.VM().add_drive(self.test_img)
 | |
|         self.vm.launch()
 | |
| 
 | |
|     def tearDown(self):
 | |
|         self.vm.shutdown()
 | |
|         os.remove(self.test_img)
 | |
|         os.remove(self.mid_img_abs)
 | |
|         os.remove(self.backing_img_abs)
 | |
|         try:
 | |
|             os.rmdir(os.path.join(iotests.test_dir, self.dir1))
 | |
|             os.rmdir(os.path.join(iotests.test_dir, self.dir3))
 | |
|             os.rmdir(os.path.join(iotests.test_dir, self.dir2))
 | |
|         except OSError as exception:
 | |
|             if exception.errno != errno.EEXIST and exception.errno != errno.ENOTEMPTY:
 | |
|                 raise
 | |
| 
 | |
|     def test_commit(self):
 | |
|         self.run_commit_test(self.mid_img, self.backing_img)
 | |
|         self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', self.backing_img_abs).find("verification failed"))
 | |
|         self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', self.backing_img_abs).find("verification failed"))
 | |
| 
 | |
|     def test_device_not_found(self):
 | |
|         result = self.vm.qmp('block-commit', device='nonexistent', top='%s' % self.mid_img)
 | |
|         self.assert_qmp(result, 'error/class', 'DeviceNotFound')
 | |
| 
 | |
|     def test_top_same_base(self):
 | |
|         self.assert_no_active_block_jobs()
 | |
|         result = self.vm.qmp('block-commit', device='drive0', top='%s' % self.mid_img, base='%s' % self.mid_img)
 | |
|         self.assert_qmp(result, 'error/class', 'GenericError')
 | |
|         self.assert_qmp(result, 'error/desc', 'Base \'%s\' not found' % self.mid_img)
 | |
| 
 | |
|     def test_top_invalid(self):
 | |
|         self.assert_no_active_block_jobs()
 | |
|         result = self.vm.qmp('block-commit', device='drive0', top='badfile', base='%s' % self.backing_img)
 | |
|         self.assert_qmp(result, 'error/class', 'GenericError')
 | |
|         self.assert_qmp(result, 'error/desc', 'Top image file badfile not found')
 | |
| 
 | |
|     def test_base_invalid(self):
 | |
|         self.assert_no_active_block_jobs()
 | |
|         result = self.vm.qmp('block-commit', device='drive0', top='%s' % self.mid_img, base='badfile')
 | |
|         self.assert_qmp(result, 'error/class', 'GenericError')
 | |
|         self.assert_qmp(result, 'error/desc', 'Base \'badfile\' not found')
 | |
| 
 | |
|     def test_top_is_active(self):
 | |
|         self.run_commit_test(self.test_img, self.backing_img)
 | |
|         self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', self.backing_img_abs).find("verification failed"))
 | |
|         self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', self.backing_img_abs).find("verification failed"))
 | |
| 
 | |
|     def test_top_and_base_reversed(self):
 | |
|         self.assert_no_active_block_jobs()
 | |
|         result = self.vm.qmp('block-commit', device='drive0', top='%s' % self.backing_img, base='%s' % self.mid_img)
 | |
|         self.assert_qmp(result, 'error/class', 'GenericError')
 | |
|         self.assert_qmp(result, 'error/desc', 'Base \'%s\' not found' % self.mid_img)
 | |
| 
 | |
| 
 | |
| class TestSetSpeed(ImageCommitTestCase):
 | |
|     image_len = 80 * 1024 * 1024 # MB
 | |
| 
 | |
|     def setUp(self):
 | |
|         qemu_img('create', backing_img, str(TestSetSpeed.image_len))
 | |
|         qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, mid_img)
 | |
|         qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % mid_img, test_img)
 | |
|         qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0x1 0 512', test_img)
 | |
|         qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xef 524288 524288', mid_img)
 | |
|         self.vm = iotests.VM().add_drive(test_img)
 | |
|         self.vm.launch()
 | |
| 
 | |
|     def tearDown(self):
 | |
|         self.vm.shutdown()
 | |
|         os.remove(test_img)
 | |
|         os.remove(mid_img)
 | |
|         os.remove(backing_img)
 | |
| 
 | |
|     def test_set_speed(self):
 | |
|         self.assert_no_active_block_jobs()
 | |
| 
 | |
|         self.vm.pause_drive('drive0')
 | |
|         result = self.vm.qmp('block-commit', device='drive0', top=mid_img, speed=1024 * 1024)
 | |
|         self.assert_qmp(result, 'return', {})
 | |
| 
 | |
|         # Ensure the speed we set was accepted
 | |
|         result = self.vm.qmp('query-block-jobs')
 | |
|         self.assert_qmp(result, 'return[0]/device', 'drive0')
 | |
|         self.assert_qmp(result, 'return[0]/speed', 1024 * 1024)
 | |
| 
 | |
|         self.cancel_and_wait(resume=True)
 | |
| 
 | |
| class TestActiveZeroLengthImage(TestSingleDrive):
 | |
|     image_len = 0
 | |
| 
 | |
| class TestReopenOverlay(ImageCommitTestCase):
 | |
|     image_len = 1024 * 1024
 | |
|     img0 = os.path.join(iotests.test_dir, '0.img')
 | |
|     img1 = os.path.join(iotests.test_dir, '1.img')
 | |
|     img2 = os.path.join(iotests.test_dir, '2.img')
 | |
|     img3 = os.path.join(iotests.test_dir, '3.img')
 | |
| 
 | |
|     def setUp(self):
 | |
|         iotests.create_image(self.img0, self.image_len)
 | |
|         qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % self.img0, self.img1)
 | |
|         qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % self.img1, self.img2)
 | |
|         qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % self.img2, self.img3)
 | |
|         qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xab 0 128K', self.img1)
 | |
|         self.vm = iotests.VM().add_drive(self.img3)
 | |
|         self.vm.launch()
 | |
| 
 | |
|     def tearDown(self):
 | |
|         self.vm.shutdown()
 | |
|         os.remove(self.img0)
 | |
|         os.remove(self.img1)
 | |
|         os.remove(self.img2)
 | |
|         os.remove(self.img3)
 | |
| 
 | |
|     # This tests what happens when the overlay image of the 'top' node
 | |
|     # needs to be reopened in read-write mode in order to update the
 | |
|     # backing image string.
 | |
|     def test_reopen_overlay(self):
 | |
|         self.run_commit_test(self.img1, self.img0)
 | |
| 
 | |
| if __name__ == '__main__':
 | |
|     iotests.main(supported_fmts=['qcow2', 'qed'])
 |