3 # Tests for image block commit.
5 # Copyright (C) 2012 IBM, Corp.
6 # Copyright (C) 2012 Red Hat, Inc.
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 2 of the License, or
11 # (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program. If not, see <http://www.gnu.org/licenses/>.
21 # Test for live block commit
22 # Derived from Image Streaming Test 030
27 from iotests
import qemu_img
, qemu_io
30 backing_img
= os
.path
.join(iotests
.test_dir
, 'backing.img')
31 mid_img
= os
.path
.join(iotests
.test_dir
, 'mid.img')
32 test_img
= os
.path
.join(iotests
.test_dir
, 'test.img')
34 class ImageCommitTestCase(iotests
.QMPTestCase
):
35 '''Abstract base class for image commit test cases'''
37 def assert_no_active_commit(self
):
38 result
= self
.vm
.qmp('query-block-jobs')
39 self
.assert_qmp(result
, 'return', [])
41 def cancel_and_wait(self
, drive
='drive0'):
42 '''Cancel a block job and wait for it to finish'''
43 result
= self
.vm
.qmp('block-job-cancel', device
=drive
)
44 self
.assert_qmp(result
, 'return', {})
48 for event
in self
.vm
.get_qmp_events(wait
=True):
49 if event
['event'] == 'BLOCK_JOB_CANCELLED':
50 self
.assert_qmp(event
, 'data/type', 'commit')
51 self
.assert_qmp(event
, 'data/device', drive
)
54 self
.assert_no_active_commit()
56 def create_image(self
, name
, size
):
57 file = open(name
, 'w')
60 sector
= struct
.pack('>l504xl', i
/ 512, i
/ 512)
66 class TestSingleDrive(ImageCommitTestCase
):
67 image_len
= 1 * 1024 * 1024
68 test_len
= 1 * 1024 * 256
71 self
.create_image(backing_img
, TestSingleDrive
.image_len
)
72 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'backing_file=%s' % backing_img
, mid_img
)
73 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'backing_file=%s' % mid_img
, test_img
)
74 qemu_io('-c', 'write -P 0xab 0 524288', backing_img
)
75 qemu_io('-c', 'write -P 0xef 524288 524288', mid_img
)
76 self
.vm
= iotests
.VM().add_drive(test_img
)
83 os
.remove(backing_img
)
85 def test_commit(self
):
86 self
.assert_no_active_commit()
87 result
= self
.vm
.qmp('block-commit', device
='drive0', top
='%s' % mid_img
)
88 self
.assert_qmp(result
, 'return', {})
92 for event
in self
.vm
.get_qmp_events(wait
=True):
93 if event
['event'] == 'BLOCK_JOB_COMPLETED':
94 self
.assert_qmp(event
, 'data/type', 'commit')
95 self
.assert_qmp(event
, 'data/device', 'drive0')
96 self
.assert_qmp(event
, 'data/offset', self
.image_len
)
97 self
.assert_qmp(event
, 'data/len', self
.image_len
)
100 self
.assert_no_active_commit()
103 self
.assertEqual(-1, qemu_io('-c', 'read -P 0xab 0 524288', backing_img
).find("verification failed"))
104 self
.assertEqual(-1, qemu_io('-c', 'read -P 0xef 524288 524288', backing_img
).find("verification failed"))
106 def test_device_not_found(self
):
107 result
= self
.vm
.qmp('block-commit', device
='nonexistent', top
='%s' % mid_img
)
108 self
.assert_qmp(result
, 'error/class', 'DeviceNotFound')
110 def test_top_same_base(self
):
111 self
.assert_no_active_commit()
112 result
= self
.vm
.qmp('block-commit', device
='drive0', top
='%s' % backing_img
, base
='%s' % backing_img
)
113 self
.assert_qmp(result
, 'error/class', 'GenericError')
114 self
.assert_qmp(result
, 'error/desc', 'Invalid files for merge: top and base are the same')
116 def test_top_invalid(self
):
117 self
.assert_no_active_commit()
118 result
= self
.vm
.qmp('block-commit', device
='drive0', top
='badfile', base
='%s' % backing_img
)
119 self
.assert_qmp(result
, 'error/class', 'GenericError')
120 self
.assert_qmp(result
, 'error/desc', 'Top image file badfile not found')
122 def test_base_invalid(self
):
123 self
.assert_no_active_commit()
124 result
= self
.vm
.qmp('block-commit', device
='drive0', top
='%s' % mid_img
, base
='badfile')
125 self
.assert_qmp(result
, 'error/class', 'GenericError')
126 self
.assert_qmp(result
, 'error/desc', 'Base \'badfile\' not found')
128 def test_top_is_active(self
):
129 self
.assert_no_active_commit()
130 result
= self
.vm
.qmp('block-commit', device
='drive0', top
='%s' % test_img
, base
='%s' % backing_img
)
131 self
.assert_qmp(result
, 'error/class', 'GenericError')
132 self
.assert_qmp(result
, 'error/desc', 'Top image as the active layer is currently unsupported')
134 def test_top_and_base_reversed(self
):
135 self
.assert_no_active_commit()
136 result
= self
.vm
.qmp('block-commit', device
='drive0', top
='%s' % backing_img
, base
='%s' % mid_img
)
137 self
.assert_qmp(result
, 'error/class', 'GenericError')
138 self
.assert_qmp(result
, 'error/desc', 'Base (%(1)s) is not reachable from top (%(2)s)' % {"1" : mid_img
, "2" : backing_img
})
140 def test_top_omitted(self
):
141 self
.assert_no_active_commit()
142 result
= self
.vm
.qmp('block-commit', device
='drive0')
143 self
.assert_qmp(result
, 'error/class', 'GenericError')
144 self
.assert_qmp(result
, 'error/desc', "Parameter 'top' is missing")
147 class TestSetSpeed(ImageCommitTestCase
):
148 image_len
= 80 * 1024 * 1024 # MB
151 qemu_img('create', backing_img
, str(TestSetSpeed
.image_len
))
152 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'backing_file=%s' % backing_img
, mid_img
)
153 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'backing_file=%s' % mid_img
, test_img
)
154 self
.vm
= iotests
.VM().add_drive(test_img
)
161 os
.remove(backing_img
)
163 def test_set_speed(self
):
164 self
.assert_no_active_commit()
166 result
= self
.vm
.qmp('block-commit', device
='drive0', top
=mid_img
, speed
=1024 * 1024)
167 self
.assert_qmp(result
, 'return', {})
169 # Ensure the speed we set was accepted
170 result
= self
.vm
.qmp('query-block-jobs')
171 self
.assert_qmp(result
, 'return[0]/device', 'drive0')
172 self
.assert_qmp(result
, 'return[0]/speed', 1024 * 1024)
174 self
.cancel_and_wait()
177 if __name__
== '__main__':
178 iotests
.main(supported_fmts
=['qcow2', 'qed'])