3 # Tests for dirty bitmaps migration.
5 # Copyright (c) 2016-2017 Virtuozzo International GmbH. All rights reserved.
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
28 from iotests
import qemu_img
31 disk_a
= os
.path
.join(iotests
.test_dir
, 'disk_a')
32 disk_b
= os
.path
.join(iotests
.test_dir
, 'disk_b')
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 class TestDirtyBitmapMigration(iotests
.QMPTestCase
):
48 qemu_img('create', '-f', iotests
.imgfmt
, disk_a
, size
)
49 qemu_img('create', '-f', iotests
.imgfmt
, disk_b
, size
)
51 self
.vm_a
= iotests
.VM(path_suffix
='a').add_drive(disk_a
)
54 self
.vm_b
= iotests
.VM(path_suffix
='b')
56 def add_bitmap(self
, vm
, granularity
, persistent
):
57 params
= {'node': 'drive0',
59 'granularity': granularity
}
61 params
['persistent'] = True
63 result
= vm
.qmp('block-dirty-bitmap-add', **params
)
64 self
.assert_qmp(result
, 'return', {});
66 def get_bitmap_hash(self
, vm
):
67 result
= vm
.qmp('x-debug-block-dirty-bitmap-sha256',
68 node
='drive0', name
='bitmap0')
69 return result
['return']['sha256']
71 def check_bitmap(self
, vm
, sha256
):
72 result
= vm
.qmp('x-debug-block-dirty-bitmap-sha256',
73 node
='drive0', name
='bitmap0')
75 self
.assert_qmp(result
, 'return/sha256', sha256
);
77 self
.assert_qmp(result
, 'error/desc',
78 "Dirty bitmap 'bitmap0' not found");
80 def do_test_migration_resume_source(self
, persistent
, migrate_bitmaps
):
83 # regions = ((start, count), ...)
84 regions
= ((0, 0x10000),
88 mig_caps
= [{'capability': 'events', 'state': True}]
90 mig_caps
.append({'capability': 'dirty-bitmaps', 'state': True})
92 result
= self
.vm_a
.qmp('migrate-set-capabilities',
93 capabilities
=mig_caps
)
94 self
.assert_qmp(result
, 'return', {})
96 self
.add_bitmap(self
.vm_a
, granularity
, persistent
)
98 self
.vm_a
.hmp_qemu_io('drive0', 'write %d %d' % r
)
99 sha256
= self
.get_bitmap_hash(self
.vm_a
)
101 result
= self
.vm_a
.qmp('migrate', uri
=mig_cmd
)
103 event
= self
.vm_a
.event_wait('MIGRATION')
104 if event
['data']['status'] == 'completed':
107 # test that bitmap is still here
108 removed
= (not migrate_bitmaps
) and persistent
109 self
.check_bitmap(self
.vm_a
, False if removed
else sha256
)
111 self
.vm_a
.qmp('cont')
113 # test that bitmap is still here after invalidation
114 self
.check_bitmap(self
.vm_a
, sha256
)
116 # shutdown and check that invalidation didn't fail
119 # catch 'Could not reopen qcow2 layer: Bitmap already exists'
121 log
= self
.vm_a
.get_log()
122 log
= re
.sub(r
'^\[I \d+\.\d+\] OPENED\n', '', log
)
123 log
= re
.sub(r
'^(wrote .* bytes at offset .*\n.*KiB.*ops.*sec.*\n){3}',
125 log
= re
.sub(r
'\[I \+\d+\.\d+\] CLOSED\n?$', '', log
)
126 self
.assertEqual(log
, '')
128 # test that bitmap is still persistent
130 self
.check_bitmap(self
.vm_a
, sha256
if persistent
else False)
132 def do_test_migration(self
, persistent
, migrate_bitmaps
, online
,
136 # regions = ((start, count), ...)
137 regions
= ((0, 0x10000),
141 should_migrate
= migrate_bitmaps
or persistent
and shared_storage
142 mig_caps
= [{'capability': 'events', 'state': True}]
144 mig_caps
.append({'capability': 'dirty-bitmaps', 'state': True})
146 result
= self
.vm_a
.qmp('migrate-set-capabilities',
147 capabilities
=mig_caps
)
148 self
.assert_qmp(result
, 'return', {})
150 self
.vm_b
.add_incoming(incoming_cmd
if online
else "defer")
151 self
.vm_b
.add_drive(disk_a
if shared_storage
else disk_b
)
156 result
= self
.vm_b
.qmp('migrate-set-capabilities',
157 capabilities
=mig_caps
)
158 self
.assert_qmp(result
, 'return', {})
160 self
.add_bitmap(self
.vm_a
, granularity
, persistent
)
162 self
.vm_a
.hmp_qemu_io('drive0', 'write %d %d' % r
)
163 sha256
= self
.get_bitmap_hash(self
.vm_a
)
165 result
= self
.vm_a
.qmp('migrate', uri
=mig_cmd
)
167 event
= self
.vm_a
.event_wait('MIGRATION')
168 if event
['data']['status'] == 'completed':
174 result
= self
.vm_b
.qmp('migrate-set-capabilities',
175 capabilities
=mig_caps
)
176 self
.assert_qmp(result
, 'return', {})
177 result
= self
.vm_b
.qmp('migrate-incoming', uri
=incoming_cmd
)
178 self
.assert_qmp(result
, 'return', {})
181 event
= self
.vm_b
.event_wait('MIGRATION')
182 if event
['data']['status'] == 'completed':
185 self
.check_bitmap(self
.vm_b
, sha256
if should_migrate
else False)
190 # catch 'Could not reopen qcow2 layer: Bitmap already exists'
192 log
= self
.vm_b
.get_log()
193 log
= re
.sub(r
'^\[I \d+\.\d+\] OPENED\n', '', log
)
194 log
= re
.sub(r
'\[I \+\d+\.\d+\] CLOSED\n?$', '', log
)
195 self
.assertEqual(log
, '')
197 # recreate vm_b, as we don't want -incoming option (this will lead
198 # to "cat" process left alive after test finish)
199 self
.vm_b
= iotests
.VM(path_suffix
='b')
200 self
.vm_b
.add_drive(disk_a
if shared_storage
else disk_b
)
202 self
.check_bitmap(self
.vm_b
, sha256
if persistent
else False)
205 def inject_test_case(klass
, name
, method
, *args
, **kwargs
):
206 mc
= operator
.methodcaller(method
, *args
, **kwargs
)
207 setattr(klass
, 'test_' + method
+ name
, new
.instancemethod(mc
, None, klass
))
209 for cmb
in list(itertools
.product((True, False), repeat
=4)):
210 name
= ('_' if cmb
[0] else '_not_') + 'persistent_'
211 name
+= ('_' if cmb
[1] else '_not_') + 'migbitmap_'
212 name
+= '_online' if cmb
[2] else '_offline'
213 name
+= '_shared' if cmb
[3] else '_nonshared'
215 inject_test_case(TestDirtyBitmapMigration
, name
, 'do_test_migration',
218 for cmb
in list(itertools
.product((True, False), repeat
=2)):
219 name
= ('_' if cmb
[0] else '_not_') + 'persistent_'
220 name
+= ('_' if cmb
[1] else '_not_') + 'migbitmap'
222 inject_test_case(TestDirtyBitmapMigration
, name
,
223 'do_test_migration_resume_source', *list(cmb
))
225 if __name__
== '__main__':
226 iotests
.main(supported_fmts
=['qcow2'])