#!/usr/bin/env python3 # # Tests for dirty bitmaps postcopy migration. # # Copyright (c) 2016-2017 Virtuozzo International GmbH. All rights reserved. # # 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 . # import os import iotests from iotests import qemu_img debug = False disk_a = os.path.join(iotests.test_dir, 'disk_a') disk_b = os.path.join(iotests.test_dir, 'disk_b') size = '256G' fifo = os.path.join(iotests.test_dir, 'mig_fifo') def event_seconds(event): return event['timestamp']['seconds'] + \ event['timestamp']['microseconds'] / 1000000.0 def event_dist(e1, e2): return event_seconds(e2) - event_seconds(e1) class TestDirtyBitmapPostcopyMigration(iotests.QMPTestCase): def tearDown(self): if debug: self.vm_a_events += self.vm_a.get_qmp_events() self.vm_b_events += self.vm_b.get_qmp_events() for e in self.vm_a_events: e['vm'] = 'SRC' for e in self.vm_b_events: e['vm'] = 'DST' events = (self.vm_a_events + self.vm_b_events) events = [(e['timestamp']['seconds'], e['timestamp']['microseconds'], e['vm'], e['event'], e.get('data', '')) for e in events] for e in sorted(events): print('{}.{:06} {} {} {}'.format(*e)) self.vm_a.shutdown() self.vm_b.shutdown() os.remove(disk_a) os.remove(disk_b) os.remove(fifo) def setUp(self): os.mkfifo(fifo) qemu_img('create', '-f', iotests.imgfmt, disk_a, size) qemu_img('create', '-f', iotests.imgfmt, disk_b, size) self.vm_a = iotests.VM(path_suffix='a').add_drive(disk_a) self.vm_b = iotests.VM(path_suffix='b').add_drive(disk_b) self.vm_b.add_incoming("exec: cat '" + fifo + "'") self.vm_a.launch() self.vm_b.launch() # collect received events for debug self.vm_a_events = [] self.vm_b_events = [] def test_postcopy(self): write_size = 0x40000000 granularity = 512 chunk = 4096 result = self.vm_a.qmp('block-dirty-bitmap-add', node='drive0', name='bitmap', granularity=granularity) self.assert_qmp(result, 'return', {}) s = 0 while s < write_size: self.vm_a.hmp_qemu_io('drive0', 'write %d %d' % (s, chunk)) s += 0x10000 s = 0x8000 while s < write_size: self.vm_a.hmp_qemu_io('drive0', 'write %d %d' % (s, chunk)) s += 0x10000 result = self.vm_a.qmp('x-debug-block-dirty-bitmap-sha256', node='drive0', name='bitmap') sha256 = result['return']['sha256'] result = self.vm_a.qmp('block-dirty-bitmap-clear', node='drive0', name='bitmap') self.assert_qmp(result, 'return', {}) s = 0 while s < write_size: self.vm_a.hmp_qemu_io('drive0', 'write %d %d' % (s, chunk)) s += 0x10000 caps = [{'capability': 'dirty-bitmaps', 'state': True}, {'capability': 'events', 'state': True}] result = self.vm_a.qmp('migrate-set-capabilities', capabilities=caps) self.assert_qmp(result, 'return', {}) result = self.vm_b.qmp('migrate-set-capabilities', capabilities=caps) self.assert_qmp(result, 'return', {}) result = self.vm_a.qmp('migrate', uri='exec:cat>' + fifo) self.assert_qmp(result, 'return', {}) result = self.vm_a.qmp('migrate-start-postcopy') self.assert_qmp(result, 'return', {}) event_resume = self.vm_b.event_wait('RESUME') self.vm_b_events.append(event_resume) s = 0x8000 while s < write_size: self.vm_b.hmp_qemu_io('drive0', 'write %d %d' % (s, chunk)) s += 0x10000 match = {'data': {'status': 'completed'}} event_complete = self.vm_b.event_wait('MIGRATION', match=match) self.vm_b_events.append(event_complete) # take queued event, should already been happened event_stop = self.vm_a.event_wait('STOP') self.vm_a_events.append(event_stop) downtime = event_dist(event_stop, event_resume) postcopy_time = event_dist(event_resume, event_complete) # TODO: assert downtime * 10 < postcopy_time if debug: print('downtime:', downtime) print('postcopy_time:', postcopy_time) # Assert that bitmap migration is finished (check that successor bitmap # is removed) result = self.vm_b.qmp('query-block') assert len(result['return'][0]['dirty-bitmaps']) == 1 # Check content of migrated (and updated by new writes) bitmap result = self.vm_b.qmp('x-debug-block-dirty-bitmap-sha256', node='drive0', name='bitmap') self.assert_qmp(result, 'return/sha256', sha256) if __name__ == '__main__': iotests.main(supported_fmts=['qcow2'])