|  | #!/usr/bin/env python3 | 
|  | # group: rw quick | 
|  | # | 
|  | # Test the rate limit of QMP events | 
|  | # | 
|  | # Copyright (C) 2016 Igalia, S.L. | 
|  | # Author: Alberto Garcia <berto@igalia.com> | 
|  | # | 
|  | # 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 <http://www.gnu.org/licenses/>. | 
|  | # | 
|  |  | 
|  | import os | 
|  | import iotests | 
|  |  | 
|  | imgs = (os.path.join(iotests.test_dir, 'quorum0.img'), | 
|  | os.path.join(iotests.test_dir, 'quorum1.img'), | 
|  | os.path.join(iotests.test_dir, 'quorum2.img')) | 
|  |  | 
|  | img_conf = (os.path.join(iotests.test_dir, 'quorum0.conf'), | 
|  | os.path.join(iotests.test_dir, 'quorum1.conf'), | 
|  | os.path.join(iotests.test_dir, 'quorum2.conf')) | 
|  |  | 
|  | event_rate = 1000000000 | 
|  | sector_size = 512 | 
|  | offset = 10 | 
|  |  | 
|  | class TestQuorumEvents(iotests.QMPTestCase): | 
|  | read_pattern = 'quorum' | 
|  |  | 
|  | def create_blkdebug_file(self, blkdebug_file, bad_sector): | 
|  | file = open(blkdebug_file, 'w') | 
|  | file.write(''' | 
|  | [inject-error] | 
|  | event = "read_aio" | 
|  | errno = "5" | 
|  | sector = "%d" | 
|  | ''' % bad_sector) | 
|  | file.close() | 
|  |  | 
|  | @iotests.skip_if_unsupported(['quorum']) | 
|  | def setUp(self): | 
|  | driveopts = ['driver=quorum', 'vote-threshold=2'] | 
|  | driveopts.append('read-pattern=%s' % self.read_pattern) | 
|  | for i in range(len(imgs)): | 
|  | iotests.qemu_img('create', '-f', iotests.imgfmt, imgs[i], '1M') | 
|  | self.create_blkdebug_file(img_conf[i], i + offset) | 
|  | driveopts.append('children.%d.driver=%s' % (i, iotests.imgfmt)) | 
|  | driveopts.append('children.%d.file.driver=blkdebug' % i) | 
|  | driveopts.append('children.%d.file.config=%s' % (i, img_conf[i])) | 
|  | driveopts.append('children.%d.file.image.filename=%s' % (i, imgs[i])) | 
|  | driveopts.append('children.%d.node-name=img%d' % (i, i)) | 
|  | self.vm = iotests.VM() | 
|  | self.vm.add_drive(None, opts = ','.join(driveopts)) | 
|  | self.vm.launch() | 
|  |  | 
|  | def tearDown(self): | 
|  | self.vm.shutdown() | 
|  | for i in range(len(imgs)): | 
|  | os.remove(imgs[i]) | 
|  | os.remove(img_conf[i]) | 
|  |  | 
|  | def do_check_event(self, node, sector = 0): | 
|  | if node == None: | 
|  | self.assertEqual(self.vm.get_qmp_event(), None) | 
|  | return | 
|  |  | 
|  | for event in self.vm.get_qmp_events(wait=True): | 
|  | if event['event'] == 'QUORUM_REPORT_BAD': | 
|  | self.assert_qmp(event, 'data/node-name', node) | 
|  | self.assert_qmp(event, 'data/sector-num', sector) | 
|  |  | 
|  | def testQuorum(self): | 
|  | # Generate an error and get an event | 
|  | self.vm.hmp_qemu_io("drive0", "aio_read %d %d" % | 
|  | (offset * sector_size, sector_size)) | 
|  | self.vm.qtest("clock_step 10") | 
|  | self.do_check_event('img0', offset) | 
|  |  | 
|  | # I/O errors in the same child: only one event is emitted | 
|  | delay = 10 | 
|  | for i in range(3): | 
|  | self.vm.hmp_qemu_io("drive0", "aio_read %d %d" % | 
|  | (offset * sector_size, sector_size)) | 
|  | self.vm.qtest("clock_step %d" % delay) | 
|  | self.do_check_event(None) | 
|  |  | 
|  | # Wait enough so the event is finally emitted | 
|  | self.vm.qtest("clock_step %d" % (2 * event_rate)) | 
|  | self.do_check_event('img0', offset) | 
|  |  | 
|  | # I/O errors in the same child: all events are emitted | 
|  | delay = 2 * event_rate | 
|  | for i in range(3): | 
|  | self.vm.hmp_qemu_io("drive0", "aio_read %d %d" % | 
|  | (offset * sector_size, sector_size)) | 
|  | self.vm.qtest("clock_step %d" % delay) | 
|  | self.do_check_event('img0', offset) | 
|  |  | 
|  | # I/O errors in different children: all events are emitted | 
|  | delay = 10 | 
|  | for i in range(len(imgs)): | 
|  | self.vm.hmp_qemu_io("drive0", "aio_read %d %d" % | 
|  | ((offset + i) * sector_size, sector_size)) | 
|  | self.vm.qtest("clock_step %d" % delay) | 
|  | # In fifo mode only errors in the first child are detected | 
|  | if i > 0 and self.read_pattern == 'fifo': | 
|  | self.do_check_event(None) | 
|  | else: | 
|  | self.do_check_event('img%d' % i, offset + i) | 
|  |  | 
|  | # I/O errors in different children: all events are emitted | 
|  | delay = 2 * event_rate | 
|  | for i in range(len(imgs)): | 
|  | self.vm.hmp_qemu_io("drive0", "aio_read %d %d" % | 
|  | ((offset + i) * sector_size, sector_size)) | 
|  | self.vm.qtest("clock_step %d" % delay) | 
|  | # In fifo mode only errors in the first child are detected | 
|  | if i > 0 and self.read_pattern == 'fifo': | 
|  | self.do_check_event(None) | 
|  | else: | 
|  | self.do_check_event('img%d' % i, offset + i) | 
|  |  | 
|  | # No more pending events | 
|  | self.do_check_event(None) | 
|  |  | 
|  | class TestFifoQuorumEvents(TestQuorumEvents): | 
|  | read_pattern = 'fifo' | 
|  |  | 
|  | if __name__ == '__main__': | 
|  | iotests.verify_quorum() | 
|  | iotests.main(supported_fmts=["raw"], | 
|  | supported_protocols=["file"]) |