]>
git.proxmox.com Git - mirror_qemu.git/blob - tests/qemu-iotests/tests/migrate-bitmaps-postcopy-test
4 # Tests for dirty bitmaps postcopy 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/>.
24 from iotests
import qemu_img
28 disk_a
= os
.path
.join(iotests
.test_dir
, 'disk_a')
29 disk_b
= os
.path
.join(iotests
.test_dir
, 'disk_b')
31 fifo
= os
.path
.join(iotests
.test_dir
, 'mig_fifo')
36 GiB
= 1024 * 1024 * 1024
40 (2 * GiB
+ 512 * 5, 512),
41 (3 * GiB
+ 512 * 5, 512),
46 (3 * GiB
+ 512 * 8, 512),
47 (4 * GiB
+ 512 * 8, 512),
49 (100 * GiB
+ GiB
// 2, GiB
)
53 def apply_discards(vm
, discards
):
55 vm
.hmp_qemu_io('drive0', 'discard {} {}'.format(*d
))
58 def event_seconds(event
):
59 return event
['timestamp']['seconds'] + \
60 event
['timestamp']['microseconds'] / 1000000.0
63 def event_dist(e1
, e2
):
64 return event_seconds(e2
) - event_seconds(e1
)
67 def check_bitmaps(vm
, count
):
68 result
= vm
.qmp('query-block')
70 info
= result
['return'][0].get('inserted', {})
73 assert 'dirty-bitmaps' not in info
75 assert len(info
['dirty-bitmaps']) == count
78 class TestDirtyBitmapPostcopyMigration(iotests
.QMPTestCase
):
81 self
.vm_a_events
+= self
.vm_a
.get_qmp_events()
82 self
.vm_b_events
+= self
.vm_b
.get_qmp_events()
83 for e
in self
.vm_a_events
:
85 for e
in self
.vm_b_events
:
87 events
= (self
.vm_a_events
+ self
.vm_b_events
)
88 events
= [(e
['timestamp']['seconds'],
89 e
['timestamp']['microseconds'],
92 e
.get('data', '')) for e
in events
]
93 for e
in sorted(events
):
94 print('{}.{:06} {} {} {}'.format(*e
))
104 qemu_img('create', '-f', iotests
.imgfmt
, disk_a
, size
)
105 qemu_img('create', '-f', iotests
.imgfmt
, disk_b
, size
)
106 self
.vm_a
= iotests
.VM(path_suffix
='a').add_drive(disk_a
,
108 self
.vm_b
= iotests
.VM(path_suffix
='b').add_drive(disk_b
,
110 self
.vm_b
.add_incoming("exec: cat '" + fifo
+ "'")
114 # collect received events for debug
115 self
.vm_a_events
= []
116 self
.vm_b_events
= []
118 def start_postcopy(self
):
119 """ Run migration until RESUME event on target. Return this event. """
120 for i
in range(nb_bitmaps
):
121 result
= self
.vm_a
.qmp('block-dirty-bitmap-add', node
='drive0',
122 name
='bitmap{}'.format(i
),
123 granularity
=granularity
,
125 self
.assert_qmp(result
, 'return', {})
127 result
= self
.vm_a
.qmp('x-debug-block-dirty-bitmap-sha256',
128 node
='drive0', name
='bitmap0')
129 empty_sha256
= result
['return']['sha256']
131 apply_discards(self
.vm_a
, discards1
)
133 result
= self
.vm_a
.qmp('x-debug-block-dirty-bitmap-sha256',
134 node
='drive0', name
='bitmap0')
135 discards1_sha256
= result
['return']['sha256']
137 # Check, that updating the bitmap by discards works
138 assert discards1_sha256
!= empty_sha256
140 # We want to calculate resulting sha256. Do it in bitmap0, so, disable
142 for i
in range(1, nb_bitmaps
):
143 result
= self
.vm_a
.qmp('block-dirty-bitmap-disable', node
='drive0',
144 name
='bitmap{}'.format(i
))
145 self
.assert_qmp(result
, 'return', {})
147 apply_discards(self
.vm_a
, discards2
)
149 result
= self
.vm_a
.qmp('x-debug-block-dirty-bitmap-sha256',
150 node
='drive0', name
='bitmap0')
151 all_discards_sha256
= result
['return']['sha256']
153 # Now, enable some bitmaps, to be updated during migration
154 for i
in range(2, nb_bitmaps
, 2):
155 result
= self
.vm_a
.qmp('block-dirty-bitmap-enable', node
='drive0',
156 name
='bitmap{}'.format(i
))
157 self
.assert_qmp(result
, 'return', {})
159 caps
= [{'capability': 'dirty-bitmaps', 'state': True},
160 {'capability': 'events', 'state': True}]
162 result
= self
.vm_a
.qmp('migrate-set-capabilities', capabilities
=caps
)
163 self
.assert_qmp(result
, 'return', {})
165 result
= self
.vm_b
.qmp('migrate-set-capabilities', capabilities
=caps
)
166 self
.assert_qmp(result
, 'return', {})
168 result
= self
.vm_a
.qmp('migrate', uri
='exec:cat>' + fifo
)
169 self
.assert_qmp(result
, 'return', {})
171 result
= self
.vm_a
.qmp('migrate-start-postcopy')
172 self
.assert_qmp(result
, 'return', {})
174 event_resume
= self
.vm_b
.event_wait('RESUME')
175 self
.vm_b_events
.append(event_resume
)
176 return (event_resume
, discards1_sha256
, all_discards_sha256
)
178 def test_postcopy_success(self
):
179 event_resume
, discards1_sha256
, all_discards_sha256
= \
180 self
.start_postcopy()
182 # enabled bitmaps should be updated
183 apply_discards(self
.vm_b
, discards2
)
185 match
= {'data': {'status': 'completed'}}
186 event_complete
= self
.vm_b
.event_wait('MIGRATION', match
=match
)
187 self
.vm_b_events
.append(event_complete
)
189 # take queued event, should already been happened
190 event_stop
= self
.vm_a
.event_wait('STOP')
191 self
.vm_a_events
.append(event_stop
)
193 downtime
= event_dist(event_stop
, event_resume
)
194 postcopy_time
= event_dist(event_resume
, event_complete
)
196 assert downtime
* 10 < postcopy_time
198 print('downtime:', downtime
)
199 print('postcopy_time:', postcopy_time
)
201 # check that there are no bitmaps stored on source
202 self
.vm_a_events
+= self
.vm_a
.get_qmp_events()
205 check_bitmaps(self
.vm_a
, 0)
207 # check that bitmaps are migrated and persistence works
208 check_bitmaps(self
.vm_b
, nb_bitmaps
)
210 # recreate vm_b, so there is no incoming option, which prevents
211 # loading bitmaps from disk
212 self
.vm_b
= iotests
.VM(path_suffix
='b').add_drive(disk_b
)
214 check_bitmaps(self
.vm_b
, nb_bitmaps
)
216 # Check content of migrated bitmaps. Still, don't waste time checking
218 for i
in range(0, nb_bitmaps
, 5):
219 result
= self
.vm_b
.qmp('x-debug-block-dirty-bitmap-sha256',
220 node
='drive0', name
='bitmap{}'.format(i
))
221 sha
= discards1_sha256
if i
% 2 else all_discards_sha256
222 self
.assert_qmp(result
, 'return/sha256', sha
)
224 def test_early_shutdown_destination(self
):
225 self
.start_postcopy()
227 self
.vm_b_events
+= self
.vm_b
.get_qmp_events()
229 # While being here, let's check that we can't remove in-flight bitmaps.
230 for vm
in (self
.vm_a
, self
.vm_b
):
231 for i
in range(0, nb_bitmaps
):
232 result
= vm
.qmp('block-dirty-bitmap-remove', node
='drive0',
234 self
.assert_qmp(result
, 'error/desc',
235 f
"Bitmap 'bitmap{i}' is currently in use by "
236 "another operation and cannot be used")
239 # recreate vm_b, so there is no incoming option, which prevents
240 # loading bitmaps from disk
241 self
.vm_b
= iotests
.VM(path_suffix
='b').add_drive(disk_b
)
243 check_bitmaps(self
.vm_b
, 0)
245 # Bitmaps will be lost if we just shutdown the vm, as they are marked
246 # to skip storing to disk when prepared for migration. And that's
247 # correct, as actual data may be modified in target vm, so we play
249 # Still, this mark would be taken away if we do 'cont', and bitmaps
250 # become persistent again. (see iotest 169 for such behavior case)
251 result
= self
.vm_a
.qmp('query-status')
252 assert not result
['return']['running']
253 self
.vm_a_events
+= self
.vm_a
.get_qmp_events()
256 check_bitmaps(self
.vm_a
, 0)
258 def test_early_kill_source(self
):
259 self
.start_postcopy()
261 self
.vm_a_events
= self
.vm_a
.get_qmp_events()
266 match
= {'data': {'status': 'completed'}}
267 e_complete
= self
.vm_b
.event_wait('MIGRATION', match
=match
)
268 self
.vm_b_events
.append(e_complete
)
270 check_bitmaps(self
.vm_a
, 0)
271 check_bitmaps(self
.vm_b
, 0)
274 if __name__
== '__main__':
275 iotests
.main(supported_fmts
=['qcow2'],
276 unsupported_imgopts
=['compat'])