| #!/usr/bin/env python3 | 
 | # group: rw | 
 | # | 
 | # Test nbd reconnect | 
 | # | 
 | # Copyright (c) 2019 Virtuozzo International GmbH. | 
 | # | 
 | # 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 time | 
 | import os | 
 |  | 
 | import iotests | 
 | from iotests import qemu_img_create, file_path, qemu_nbd_popen | 
 |  | 
 | disk_a, disk_b = file_path('disk_a', 'disk_b') | 
 | nbd_sock = file_path('nbd-sock', base_dir=iotests.sock_dir) | 
 | nbd_uri = 'nbd+unix:///?socket=' + nbd_sock | 
 | wait_limit = 3.0 | 
 | wait_step = 0.2 | 
 |  | 
 |  | 
 | class TestNbdReconnect(iotests.QMPTestCase): | 
 |     def init_vm(self, disk_size): | 
 |         qemu_img_create('-f', iotests.imgfmt, disk_a, str(disk_size)) | 
 |         qemu_img_create('-f', iotests.imgfmt, disk_b, str(disk_size)) | 
 |         self.vm = iotests.VM().add_drive(disk_a) | 
 |         self.vm.launch() | 
 |         self.vm.hmp_qemu_io('drive0', 'write 0 {}'.format(disk_size)) | 
 |  | 
 |     def tearDown(self): | 
 |         self.vm.shutdown() | 
 |         os.remove(disk_a) | 
 |         os.remove(disk_b) | 
 |  | 
 |     def start_job(self, job): | 
 |         """Stat job with nbd target and kill the server""" | 
 |         assert job in ('blockdev-backup', 'blockdev-mirror') | 
 |         with qemu_nbd_popen('-k', nbd_sock, '-f', iotests.imgfmt, disk_b): | 
 |             self.vm.cmd('blockdev-add', | 
 |                         {'node-name': 'backup0', | 
 |                          'driver': 'raw', | 
 |                          'file': {'driver': 'nbd', | 
 |                                   'server': {'type': 'unix', | 
 |                                              'path': nbd_sock}, | 
 |                                   'reconnect-delay': 10}}) | 
 |             self.vm.cmd(job, device='drive0', | 
 |                         sync='full', target='backup0', | 
 |                         speed=(1 * 1024 * 1024)) | 
 |  | 
 |             # Wait for some progress | 
 |             t = 0.0 | 
 |             while t < wait_limit: | 
 |                 jobs = self.vm.qmp('query-block-jobs')['return'] | 
 |                 if jobs and jobs[0]['offset'] > 0: | 
 |                     break | 
 |                 time.sleep(wait_step) | 
 |                 t += wait_step | 
 |  | 
 |             self.assertTrue(jobs and jobs[0]['offset'] > 0)  # job started | 
 |  | 
 |         jobs = self.vm.qmp('query-block-jobs')['return'] | 
 |         # Check that job is still in progress | 
 |         self.assertTrue(jobs) | 
 |         self.assertTrue(jobs[0]['offset'] < jobs[0]['len']) | 
 |  | 
 |         self.vm.cmd('block-job-set-speed', device='drive0', speed=0) | 
 |  | 
 |         # Emulate server down time for 1 second | 
 |         time.sleep(1) | 
 |  | 
 |     def test_backup(self): | 
 |         size = 5 * 1024 * 1024 | 
 |         self.init_vm(size) | 
 |         self.start_job('blockdev-backup') | 
 |  | 
 |         with qemu_nbd_popen('-k', nbd_sock, '-f', iotests.imgfmt, disk_b): | 
 |             e = self.vm.event_wait('BLOCK_JOB_COMPLETED') | 
 |             self.assertEqual(e['data']['offset'], size) | 
 |             self.vm.cmd('blockdev-del', node_name='backup0') | 
 |  | 
 |     def cancel_job(self): | 
 |         self.vm.cmd('block-job-cancel', device='drive0', force=True) | 
 |  | 
 |         start_t = time.time() | 
 |         self.vm.event_wait('BLOCK_JOB_CANCELLED') | 
 |         delta_t = time.time() - start_t | 
 |         self.assertTrue(delta_t < 5.0) | 
 |  | 
 |     def test_mirror_cancel(self): | 
 |         # Mirror speed limit doesn't work well enough, it seems that mirror | 
 |         # will run many parallel requests anyway. MAX_IN_FLIGHT is 16 and | 
 |         # MAX_IO_BYTES is 1M in mirror.c, so let's use 20M disk. | 
 |         self.init_vm(20 * 1024 * 1024) | 
 |         self.start_job('blockdev-mirror') | 
 |         self.cancel_job() | 
 |  | 
 |     def test_backup_cancel(self): | 
 |         self.init_vm(5 * 1024 * 1024) | 
 |         self.start_job('blockdev-backup') | 
 |         self.cancel_job() | 
 |  | 
 |  | 
 | if __name__ == '__main__': | 
 |     iotests.main(supported_fmts=['qcow2']) |