]>
git.proxmox.com Git - mirror_qemu.git/blob - tests/qemu-iotests/tests/migrate-bitmaps-test
4 # Tests for dirty bitmaps migration.
6 # Copyright (c) 2016-2017 Virtuozzo International GmbH. All rights reserved.
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/>.
27 from iotests
import qemu_img
, qemu_img_create
, Timeout
30 disk_a
= os
.path
.join(iotests
.test_dir
, 'disk_a')
31 disk_b
= os
.path
.join(iotests
.test_dir
, 'disk_b')
32 base_a
= os
.path
.join(iotests
.test_dir
, 'base_a')
34 mig_file
= os
.path
.join(iotests
.test_dir
, 'mig_file')
35 mig_cmd
= 'exec: cat > ' + mig_file
36 incoming_cmd
= 'exec: cat ' + mig_file
39 def get_bitmap_hash(vm
):
40 result
= vm
.qmp('x-debug-block-dirty-bitmap-sha256',
41 node
='drive0', name
='bitmap0')
42 return result
['return']['sha256']
45 class TestDirtyBitmapMigration(iotests
.QMPTestCase
):
54 qemu_img('create', '-f', iotests
.imgfmt
, disk_a
, size
)
55 qemu_img('create', '-f', iotests
.imgfmt
, disk_b
, size
)
57 self
.vm_a
= iotests
.VM(path_suffix
='a').add_drive(disk_a
)
60 self
.vm_b
= iotests
.VM(path_suffix
='b')
62 def add_bitmap(self
, vm
, granularity
, persistent
):
63 params
= {'node': 'drive0',
65 'granularity': granularity
}
67 params
['persistent'] = True
69 result
= vm
.qmp('block-dirty-bitmap-add', **params
)
70 self
.assert_qmp(result
, 'return', {})
72 def check_bitmap(self
, vm
, sha256
):
73 result
= vm
.qmp('x-debug-block-dirty-bitmap-sha256',
74 node
='drive0', name
='bitmap0')
76 self
.assert_qmp(result
, 'return/sha256', sha256
)
78 self
.assert_qmp(result
, 'error/desc',
79 "Dirty bitmap 'bitmap0' not found")
81 def do_test_migration_resume_source(self
, persistent
, migrate_bitmaps
):
84 # regions = ((start, count), ...)
85 regions
= ((0, 0x10000),
89 mig_caps
= [{'capability': 'events', 'state': True}]
91 mig_caps
.append({'capability': 'dirty-bitmaps', 'state': True})
93 result
= self
.vm_a
.qmp('migrate-set-capabilities',
94 capabilities
=mig_caps
)
95 self
.assert_qmp(result
, 'return', {})
97 self
.add_bitmap(self
.vm_a
, granularity
, persistent
)
99 self
.vm_a
.hmp_qemu_io('drive0', 'write %d %d' % r
)
100 sha256
= get_bitmap_hash(self
.vm_a
)
102 result
= self
.vm_a
.qmp('migrate', uri
=mig_cmd
)
104 event
= self
.vm_a
.event_wait('MIGRATION')
105 if event
['data']['status'] == 'completed':
108 result
= self
.vm_a
.qmp('query-status')
109 if result
['return']['status'] == 'postmigrate':
112 # test that bitmap is still here
113 removed
= (not migrate_bitmaps
) and persistent
114 self
.check_bitmap(self
.vm_a
, False if removed
else sha256
)
116 result
= self
.vm_a
.qmp('cont')
117 self
.assert_qmp(result
, 'return', {})
119 # test that bitmap is still here after invalidation
120 self
.check_bitmap(self
.vm_a
, sha256
)
122 # shutdown and check that invalidation didn't fail
125 # catch 'Could not reopen qcow2 layer: Bitmap already exists'
127 log
= self
.vm_a
.get_log()
128 log
= re
.sub(r
'^\[I \d+\.\d+\] OPENED\n', '', log
)
129 log
= re
.sub(r
'^(wrote .* bytes at offset .*\n.*KiB.*ops.*sec.*\n){3}',
131 log
= re
.sub(r
'\[I \+\d+\.\d+\] CLOSED\n?$', '', log
)
132 self
.assertEqual(log
, '')
134 # test that bitmap is still persistent
136 self
.check_bitmap(self
.vm_a
, sha256
if persistent
else False)
138 def do_test_migration(self
, persistent
, migrate_bitmaps
, online
,
139 shared_storage
, pre_shutdown
):
142 # regions = ((start, count), ...)
143 regions
= ((0, 0x10000),
148 (migrate_bitmaps
and (persistent
or not pre_shutdown
)) or \
149 (persistent
and shared_storage
)
150 mig_caps
= [{'capability': 'events', 'state': True}]
152 mig_caps
.append({'capability': 'dirty-bitmaps', 'state': True})
154 self
.vm_b
.add_incoming(incoming_cmd
if online
else "defer")
155 self
.vm_b
.add_drive(disk_a
if shared_storage
else disk_b
)
160 result
= self
.vm_b
.qmp('migrate-set-capabilities',
161 capabilities
=mig_caps
)
162 self
.assert_qmp(result
, 'return', {})
164 self
.add_bitmap(self
.vm_a
, granularity
, persistent
)
166 self
.vm_a
.hmp_qemu_io('drive0', 'write %d %d' % r
)
167 sha256
= get_bitmap_hash(self
.vm_a
)
173 result
= self
.vm_a
.qmp('migrate-set-capabilities',
174 capabilities
=mig_caps
)
175 self
.assert_qmp(result
, 'return', {})
177 result
= self
.vm_a
.qmp('migrate', uri
=mig_cmd
)
179 event
= self
.vm_a
.event_wait('MIGRATION')
180 if event
['data']['status'] == 'completed':
186 result
= self
.vm_b
.qmp('migrate-set-capabilities',
187 capabilities
=mig_caps
)
188 self
.assert_qmp(result
, 'return', {})
189 result
= self
.vm_b
.qmp('migrate-incoming', uri
=incoming_cmd
)
190 self
.assert_qmp(result
, 'return', {})
193 event
= self
.vm_b
.event_wait('MIGRATION')
194 if event
['data']['status'] == 'completed':
197 self
.check_bitmap(self
.vm_b
, sha256
if should_migrate
else False)
202 # catch 'Could not reopen qcow2 layer: Bitmap already exists'
204 log
= self
.vm_b
.get_log()
205 log
= re
.sub(r
'^\[I \d+\.\d+\] OPENED\n', '', log
)
206 log
= re
.sub(r
'\[I \+\d+\.\d+\] CLOSED\n?$', '', log
)
207 self
.assertEqual(log
, '')
209 # recreate vm_b, as we don't want -incoming option (this will lead
210 # to "cat" process left alive after test finish)
211 self
.vm_b
= iotests
.VM(path_suffix
='b')
212 self
.vm_b
.add_drive(disk_a
if shared_storage
else disk_b
)
214 self
.check_bitmap(self
.vm_b
, sha256
if persistent
else False)
217 def inject_test_case(klass
, suffix
, method
, *args
, **kwargs
):
218 mc
= operator
.methodcaller(method
, *args
, **kwargs
)
219 # We want to add a function attribute to `klass`, so that it is
220 # correctly converted to a method on instantiation. The
221 # methodcaller object `mc` is a callable, not a function, so we
222 # need the lambda to turn it into a function.
223 # pylint: disable=unnecessary-lambda
224 setattr(klass
, 'test_' + method
+ suffix
, lambda self
: mc(self
))
227 for cmb
in list(itertools
.product((True, False), repeat
=5)):
228 name
= ('_' if cmb
[0] else '_not_') + 'persistent_'
229 name
+= ('_' if cmb
[1] else '_not_') + 'migbitmap_'
230 name
+= '_online' if cmb
[2] else '_offline'
231 name
+= '_shared' if cmb
[3] else '_nonshared'
233 name
+= '__pre_shutdown'
235 inject_test_case(TestDirtyBitmapMigration
, name
, 'do_test_migration',
238 for cmb
in list(itertools
.product((True, False), repeat
=2)):
239 name
= ('_' if cmb
[0] else '_not_') + 'persistent_'
240 name
+= ('_' if cmb
[1] else '_not_') + 'migbitmap'
242 inject_test_case(TestDirtyBitmapMigration
, name
,
243 'do_test_migration_resume_source', *list(cmb
))
246 class TestDirtyBitmapBackingMigration(iotests
.QMPTestCase
):
248 qemu_img_create('-f', iotests
.imgfmt
, base_a
, size
)
249 qemu_img_create('-f', iotests
.imgfmt
, '-F', iotests
.imgfmt
,
250 '-b', base_a
, disk_a
, size
)
252 for f
in (disk_a
, base_a
):
253 qemu_img('bitmap', '--add', f
, 'bmap0')
256 'node-name': 'node0',
257 'driver': iotests
.imgfmt
,
263 'node-name': 'node0-base',
264 'driver': iotests
.imgfmt
,
272 self
.vm
= iotests
.VM()
275 result
= self
.vm
.qmp('blockdev-add', **blockdev
)
276 self
.assert_qmp(result
, 'return', {})
278 # Check that the bitmaps are there
279 nodes
= self
.vm
.qmp('query-named-block-nodes', flat
=True)['return']
281 if 'node0' in node
['node-name']:
282 self
.assert_qmp(node
, 'dirty-bitmaps[0]/name', 'bmap0')
284 caps
= [{'capability': 'events', 'state': True}]
285 result
= self
.vm
.qmp('migrate-set-capabilities', capabilities
=caps
)
286 self
.assert_qmp(result
, 'return', {})
290 for f
in (disk_a
, base_a
):
293 def test_cont_on_source(self
):
295 Continue the source after migration.
297 result
= self
.vm
.qmp('migrate', uri
='exec: cat > /dev/null')
298 self
.assert_qmp(result
, 'return', {})
300 with
Timeout(10, 'Migration timeout'):
301 self
.vm
.wait_migration('postmigrate')
303 result
= self
.vm
.qmp('cont')
304 self
.assert_qmp(result
, 'return', {})
307 if __name__
== '__main__':
308 iotests
.main(supported_fmts
=['qcow2'],
309 supported_protocols
=['file'])