| #!/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"]) |