]>
Commit | Line | Data |
---|---|---|
dd3e97df VSO |
1 | #!/usr/bin/env python3 |
2 | # group: auto backup | |
3 | # | |
4 | # Copyright (c) 2022 Virtuozzo International GmbH | |
5 | # | |
6 | # This program is free software; you can redistribute it and/or modify | |
7 | # it under the terms of the GNU General Public License as published by | |
8 | # the Free Software Foundation; either version 2 of the License, or | |
9 | # (at your option) any later version. | |
10 | # | |
11 | # This program is distributed in the hope that it will be useful, | |
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | # GNU General Public License for more details. | |
15 | # | |
16 | # You should have received a copy of the GNU General Public License | |
17 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
18 | # | |
19 | ||
20 | import os | |
21 | import re | |
22 | ||
23 | from qemu.machine import QEMUMachine | |
24 | ||
25 | import iotests | |
26 | from iotests import qemu_img_create, qemu_io | |
27 | ||
28 | ||
29 | temp_img = os.path.join(iotests.test_dir, 'temp') | |
30 | source_img = os.path.join(iotests.test_dir, 'source') | |
31 | size = '1M' | |
32 | ||
33 | ||
34 | class TestCbwError(iotests.QMPTestCase): | |
35 | def tearDown(self): | |
36 | self.vm.shutdown() | |
37 | os.remove(temp_img) | |
38 | os.remove(source_img) | |
39 | ||
40 | def setUp(self): | |
41 | qemu_img_create('-f', iotests.imgfmt, source_img, size) | |
42 | qemu_img_create('-f', iotests.imgfmt, temp_img, size) | |
43 | qemu_io('-c', 'write 0 1M', source_img) | |
44 | ||
45 | opts = ['-nodefaults', '-display', 'none', '-machine', 'none'] | |
46 | self.vm = QEMUMachine(iotests.qemu_prog, opts, | |
46d4747a | 47 | base_temp_dir=iotests.test_dir) |
dd3e97df VSO |
48 | self.vm.launch() |
49 | ||
50 | def do_cbw_error(self, on_cbw_error): | |
51 | result = self.vm.qmp('blockdev-add', { | |
52 | 'node-name': 'cbw', | |
53 | 'driver': 'copy-before-write', | |
54 | 'on-cbw-error': on_cbw_error, | |
55 | 'file': { | |
56 | 'driver': iotests.imgfmt, | |
57 | 'file': { | |
58 | 'driver': 'file', | |
59 | 'filename': source_img, | |
60 | } | |
61 | }, | |
62 | 'target': { | |
63 | 'driver': iotests.imgfmt, | |
64 | 'file': { | |
65 | 'driver': 'blkdebug', | |
66 | 'image': { | |
67 | 'driver': 'file', | |
68 | 'filename': temp_img | |
69 | }, | |
70 | 'inject-error': [ | |
71 | { | |
72 | 'event': 'write_aio', | |
73 | 'errno': 5, | |
74 | 'immediately': False, | |
75 | 'once': True | |
76 | } | |
77 | ] | |
78 | } | |
79 | } | |
80 | }) | |
81 | self.assert_qmp(result, 'return', {}) | |
82 | ||
83 | result = self.vm.qmp('blockdev-add', { | |
84 | 'node-name': 'access', | |
85 | 'driver': 'snapshot-access', | |
86 | 'file': 'cbw' | |
87 | }) | |
88 | self.assert_qmp(result, 'return', {}) | |
89 | ||
90 | result = self.vm.qmp('human-monitor-command', | |
91 | command_line='qemu-io cbw "write 0 1M"') | |
92 | self.assert_qmp(result, 'return', '') | |
93 | ||
94 | result = self.vm.qmp('human-monitor-command', | |
95 | command_line='qemu-io access "read 0 1M"') | |
96 | self.assert_qmp(result, 'return', '') | |
97 | ||
98 | self.vm.shutdown() | |
99 | log = self.vm.get_log() | |
100 | log = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', log) | |
101 | log = re.sub(r'\[I \+\d+\.\d+\] CLOSED\n?$', '', log) | |
102 | log = iotests.filter_qemu_io(log) | |
103 | return log | |
104 | ||
105 | def test_break_snapshot_on_cbw_error(self): | |
106 | """break-snapshot behavior: | |
107 | Guest write succeed, but further snapshot-read fails, as snapshot is | |
108 | broken. | |
109 | """ | |
110 | log = self.do_cbw_error('break-snapshot') | |
111 | ||
112 | self.assertEqual(log, """\ | |
113 | wrote 1048576/1048576 bytes at offset 0 | |
114 | 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | |
115 | read failed: Permission denied | |
116 | """) | |
117 | ||
118 | def test_break_guest_write_on_cbw_error(self): | |
119 | """break-guest-write behavior: | |
120 | Guest write fails, but snapshot-access continues working and further | |
121 | snapshot-read succeeds. | |
122 | """ | |
123 | log = self.do_cbw_error('break-guest-write') | |
124 | ||
125 | self.assertEqual(log, """\ | |
126 | write failed: Input/output error | |
127 | read 1048576/1048576 bytes at offset 0 | |
128 | 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | |
129 | """) | |
130 | ||
9d05a87b VSO |
131 | def do_cbw_timeout(self, on_cbw_error): |
132 | result = self.vm.qmp('object-add', { | |
133 | 'qom-type': 'throttle-group', | |
134 | 'id': 'group0', | |
135 | 'limits': {'bps-write': 300 * 1024} | |
136 | }) | |
137 | self.assert_qmp(result, 'return', {}) | |
138 | ||
139 | result = self.vm.qmp('blockdev-add', { | |
140 | 'node-name': 'cbw', | |
141 | 'driver': 'copy-before-write', | |
142 | 'on-cbw-error': on_cbw_error, | |
143 | 'cbw-timeout': 1, | |
144 | 'file': { | |
145 | 'driver': iotests.imgfmt, | |
146 | 'file': { | |
147 | 'driver': 'file', | |
148 | 'filename': source_img, | |
149 | } | |
150 | }, | |
151 | 'target': { | |
152 | 'driver': 'throttle', | |
153 | 'throttle-group': 'group0', | |
154 | 'file': { | |
155 | 'driver': 'qcow2', | |
156 | 'file': { | |
157 | 'driver': 'file', | |
158 | 'filename': temp_img | |
159 | } | |
160 | } | |
161 | } | |
162 | }) | |
163 | self.assert_qmp(result, 'return', {}) | |
164 | ||
165 | result = self.vm.qmp('blockdev-add', { | |
166 | 'node-name': 'access', | |
167 | 'driver': 'snapshot-access', | |
168 | 'file': 'cbw' | |
169 | }) | |
170 | self.assert_qmp(result, 'return', {}) | |
171 | ||
172 | result = self.vm.qmp('human-monitor-command', | |
173 | command_line='qemu-io cbw "write 0 512K"') | |
174 | self.assert_qmp(result, 'return', '') | |
175 | ||
176 | # We need second write to trigger throttling | |
177 | result = self.vm.qmp('human-monitor-command', | |
178 | command_line='qemu-io cbw "write 512K 512K"') | |
179 | self.assert_qmp(result, 'return', '') | |
180 | ||
181 | result = self.vm.qmp('human-monitor-command', | |
182 | command_line='qemu-io access "read 0 1M"') | |
183 | self.assert_qmp(result, 'return', '') | |
184 | ||
185 | self.vm.shutdown() | |
186 | log = self.vm.get_log() | |
187 | log = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', log) | |
188 | log = re.sub(r'\[I \+\d+\.\d+\] CLOSED\n?$', '', log) | |
189 | log = iotests.filter_qemu_io(log) | |
190 | return log | |
191 | ||
192 | def test_timeout_break_guest(self): | |
193 | log = self.do_cbw_timeout('break-guest-write') | |
05b47eec VSO |
194 | # macOS and FreeBSD tend to represent ETIMEDOUT as |
195 | # "Operation timed out", when Linux prefer | |
196 | # "Connection timed out" | |
197 | log = log.replace('Operation timed out', | |
198 | 'Connection timed out') | |
9d05a87b VSO |
199 | self.assertEqual(log, """\ |
200 | wrote 524288/524288 bytes at offset 0 | |
201 | 512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | |
202 | write failed: Connection timed out | |
203 | read 1048576/1048576 bytes at offset 0 | |
204 | 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | |
205 | """) | |
206 | ||
207 | def test_timeout_break_snapshot(self): | |
208 | log = self.do_cbw_timeout('break-snapshot') | |
209 | self.assertEqual(log, """\ | |
210 | wrote 524288/524288 bytes at offset 0 | |
211 | 512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | |
212 | wrote 524288/524288 bytes at offset 524288 | |
213 | 512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | |
214 | read failed: Permission denied | |
215 | """) | |
216 | ||
dd3e97df VSO |
217 | |
218 | if __name__ == '__main__': | |
219 | iotests.main(supported_fmts=['qcow2'], | |
9548cbef VSO |
220 | supported_protocols=['file'], |
221 | required_fmts=['copy-before-write']) |