4 # Test what happens when errors occur to a mirror job after it has
5 # been cancelled in the READY phase
7 # Copyright (C) 2021 Red Hat, Inc.
9 # This program is free software; you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 2 of the License, or
12 # (at your option) any later version.
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
19 # You should have received a copy of the GNU General Public License
20 # along with this program. If not, see <http://www.gnu.org/licenses/>.
27 image_size = 1 * 1024 * 1024
28 source = os.path.join(iotests.test_dir, 'source.img')
29 target = os.path.join(iotests.test_dir, 'target.img')
32 class TestMirrorReadyCancelError(iotests.QMPTestCase):
33 def setUp(self) -> None:
34 iotests.qemu_img_create('-f', iotests.imgfmt, source, str(image_size))
35 iotests.qemu_img_create('-f', iotests.imgfmt, target, str(image_size))
37 # Ensure that mirror will copy something before READY so the
38 # target format layer will forward the pre-READY flush to its
40 iotests.qemu_io('-c', 'write -P 1 0 64k', source)
42 self.vm = iotests.VM()
45 def tearDown(self) -> None:
50 def add_blockdevs(self, once: bool) -> None:
51 self.vm.cmd('blockdev-add',
52 {'node-name': 'source',
53 'driver': iotests.imgfmt,
60 # Enter state 2 on the first flush, which happens before the
61 # job enters the READY state. The second flush will happen
62 # when the job is about to complete, and we want that one to
64 self.vm.cmd('blockdev-add',
65 {'node-name': 'target',
66 'driver': iotests.imgfmt,
74 'event': 'flush_to_disk',
79 'event': 'flush_to_disk',
85 def start_mirror(self) -> None:
86 self.vm.cmd('blockdev-mirror',
90 filter_node_name='mirror-top',
92 on_target_error='stop')
94 def cancel_mirror_with_error(self) -> None:
95 self.vm.event_wait('BLOCK_JOB_READY')
97 # Write something so will not leave the job immediately, but
98 # flush first (which will fail, thanks to blkdebug)
99 res = self.vm.qmp('human-monitor-command',
100 command_line='qemu-io mirror-top "write -P 2 0 64k"')
101 self.assert_qmp(res, 'return', '')
103 # Drain status change events
104 while self.vm.event_wait('JOB_STATUS_CHANGE', timeout=0.0) is not None:
107 self.vm.cmd('block-job-cancel', device='mirror')
109 self.vm.event_wait('BLOCK_JOB_ERROR')
111 def test_transient_error(self) -> None:
112 self.add_blockdevs(True)
114 self.cancel_mirror_with_error()
117 e = self.vm.event_wait('JOB_STATUS_CHANGE')
118 if e['data']['status'] == 'standby':
119 # Transient error, try again
120 self.vm.qmp('block-job-resume', device='mirror')
121 elif e['data']['status'] == 'null':
124 def test_persistent_error(self) -> None:
125 self.add_blockdevs(False)
127 self.cancel_mirror_with_error()
130 e = self.vm.event_wait('JOB_STATUS_CHANGE')
131 if e['data']['status'] == 'standby':
132 # Persistent error, no point in continuing
133 self.vm.qmp('block-job-cancel', device='mirror', force=True)
134 elif e['data']['status'] == 'null':
138 if __name__ == '__main__':
139 # LUKS would require special key-secret handling in add_blockdevs()
140 iotests.main(supported_fmts=['generic'],
141 unsupported_fmts=['luks'],
142 supported_protocols=['file'])