]>
Commit | Line | Data |
---|---|---|
903cb1bf | 1 | #!/usr/bin/env python3 |
33dac6f3 VSO |
2 | # |
3 | # Tests for dirty bitmaps migration. | |
4 | # | |
5 | # Copyright (c) 2016-2017 Virtuozzo International GmbH. All rights reserved. | |
6 | # | |
7 | # This program is free software; you can redistribute it and/or modify | |
8 | # it under the terms of the GNU General Public License as published by | |
9 | # the Free Software Foundation; either version 2 of the License, or | |
10 | # (at your option) any later version. | |
11 | # | |
12 | # This program is distributed in the hope that it will be useful, | |
13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | # GNU General Public License for more details. | |
16 | # | |
17 | # You should have received a copy of the GNU General Public License | |
18 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
19 | # | |
20 | ||
21 | import os | |
22 | import iotests | |
23 | import time | |
24 | import itertools | |
25 | import operator | |
b9247fc1 | 26 | import re |
33dac6f3 VSO |
27 | from iotests import qemu_img |
28 | ||
29 | ||
30 | disk_a = os.path.join(iotests.test_dir, 'disk_a') | |
31 | disk_b = os.path.join(iotests.test_dir, 'disk_b') | |
32 | size = '1M' | |
33 | mig_file = os.path.join(iotests.test_dir, 'mig_file') | |
25bf2426 VSO |
34 | mig_cmd = 'exec: cat > ' + mig_file |
35 | incoming_cmd = 'exec: cat ' + mig_file | |
33dac6f3 VSO |
36 | |
37 | ||
38 | class TestDirtyBitmapMigration(iotests.QMPTestCase): | |
39 | def tearDown(self): | |
40 | self.vm_a.shutdown() | |
41 | self.vm_b.shutdown() | |
42 | os.remove(disk_a) | |
43 | os.remove(disk_b) | |
44 | os.remove(mig_file) | |
45 | ||
46 | def setUp(self): | |
47 | qemu_img('create', '-f', iotests.imgfmt, disk_a, size) | |
48 | qemu_img('create', '-f', iotests.imgfmt, disk_b, size) | |
49 | ||
50 | self.vm_a = iotests.VM(path_suffix='a').add_drive(disk_a) | |
51 | self.vm_a.launch() | |
52 | ||
53 | self.vm_b = iotests.VM(path_suffix='b') | |
33dac6f3 VSO |
54 | |
55 | def add_bitmap(self, vm, granularity, persistent): | |
56 | params = {'node': 'drive0', | |
57 | 'name': 'bitmap0', | |
58 | 'granularity': granularity} | |
59 | if persistent: | |
60 | params['persistent'] = True | |
33dac6f3 VSO |
61 | |
62 | result = vm.qmp('block-dirty-bitmap-add', **params) | |
63 | self.assert_qmp(result, 'return', {}); | |
64 | ||
65 | def get_bitmap_hash(self, vm): | |
66 | result = vm.qmp('x-debug-block-dirty-bitmap-sha256', | |
67 | node='drive0', name='bitmap0') | |
68 | return result['return']['sha256'] | |
69 | ||
70 | def check_bitmap(self, vm, sha256): | |
71 | result = vm.qmp('x-debug-block-dirty-bitmap-sha256', | |
72 | node='drive0', name='bitmap0') | |
73 | if sha256: | |
74 | self.assert_qmp(result, 'return/sha256', sha256); | |
75 | else: | |
76 | self.assert_qmp(result, 'error/desc', | |
77 | "Dirty bitmap 'bitmap0' not found"); | |
78 | ||
3e6d88f2 VSO |
79 | def do_test_migration_resume_source(self, persistent, migrate_bitmaps): |
80 | granularity = 512 | |
81 | ||
82 | # regions = ((start, count), ...) | |
83 | regions = ((0, 0x10000), | |
84 | (0xf0000, 0x10000), | |
85 | (0xa0201, 0x1000)) | |
86 | ||
87 | mig_caps = [{'capability': 'events', 'state': True}] | |
88 | if migrate_bitmaps: | |
89 | mig_caps.append({'capability': 'dirty-bitmaps', 'state': True}) | |
90 | ||
91 | result = self.vm_a.qmp('migrate-set-capabilities', | |
92 | capabilities=mig_caps) | |
93 | self.assert_qmp(result, 'return', {}) | |
94 | ||
95 | self.add_bitmap(self.vm_a, granularity, persistent) | |
96 | for r in regions: | |
97 | self.vm_a.hmp_qemu_io('drive0', 'write %d %d' % r) | |
98 | sha256 = self.get_bitmap_hash(self.vm_a) | |
99 | ||
100 | result = self.vm_a.qmp('migrate', uri=mig_cmd) | |
101 | while True: | |
102 | event = self.vm_a.event_wait('MIGRATION') | |
103 | if event['data']['status'] == 'completed': | |
104 | break | |
832d78ca VSO |
105 | while True: |
106 | result = self.vm_a.qmp('query-status') | |
107 | if (result['return']['status'] == 'postmigrate'): | |
108 | break | |
3e6d88f2 VSO |
109 | |
110 | # test that bitmap is still here | |
111 | removed = (not migrate_bitmaps) and persistent | |
112 | self.check_bitmap(self.vm_a, False if removed else sha256) | |
113 | ||
832d78ca VSO |
114 | result = self.vm_a.qmp('cont') |
115 | self.assert_qmp(result, 'return', {}) | |
3e6d88f2 VSO |
116 | |
117 | # test that bitmap is still here after invalidation | |
118 | self.check_bitmap(self.vm_a, sha256) | |
119 | ||
120 | # shutdown and check that invalidation didn't fail | |
121 | self.vm_a.shutdown() | |
122 | ||
123 | # catch 'Could not reopen qcow2 layer: Bitmap already exists' | |
124 | # possible error | |
125 | log = self.vm_a.get_log() | |
126 | log = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', log) | |
127 | log = re.sub(r'^(wrote .* bytes at offset .*\n.*KiB.*ops.*sec.*\n){3}', | |
128 | '', log) | |
129 | log = re.sub(r'\[I \+\d+\.\d+\] CLOSED\n?$', '', log) | |
130 | self.assertEqual(log, '') | |
131 | ||
132 | # test that bitmap is still persistent | |
133 | self.vm_a.launch() | |
134 | self.check_bitmap(self.vm_a, sha256 if persistent else False) | |
135 | ||
33dac6f3 | 136 | def do_test_migration(self, persistent, migrate_bitmaps, online, |
d8130f4c | 137 | shared_storage, pre_shutdown): |
33dac6f3 VSO |
138 | granularity = 512 |
139 | ||
140 | # regions = ((start, count), ...) | |
141 | regions = ((0, 0x10000), | |
142 | (0xf0000, 0x10000), | |
143 | (0xa0201, 0x1000)) | |
144 | ||
d8130f4c VSO |
145 | should_migrate = \ |
146 | (migrate_bitmaps and (persistent or not pre_shutdown)) or \ | |
147 | (persistent and shared_storage) | |
25bf2426 VSO |
148 | mig_caps = [{'capability': 'events', 'state': True}] |
149 | if migrate_bitmaps: | |
150 | mig_caps.append({'capability': 'dirty-bitmaps', 'state': True}) | |
33dac6f3 | 151 | |
25bf2426 | 152 | self.vm_b.add_incoming(incoming_cmd if online else "defer") |
33dac6f3 VSO |
153 | self.vm_b.add_drive(disk_a if shared_storage else disk_b) |
154 | ||
155 | if online: | |
156 | os.mkfifo(mig_file) | |
157 | self.vm_b.launch() | |
25bf2426 VSO |
158 | result = self.vm_b.qmp('migrate-set-capabilities', |
159 | capabilities=mig_caps) | |
160 | self.assert_qmp(result, 'return', {}) | |
33dac6f3 VSO |
161 | |
162 | self.add_bitmap(self.vm_a, granularity, persistent) | |
163 | for r in regions: | |
164 | self.vm_a.hmp_qemu_io('drive0', 'write %d %d' % r) | |
165 | sha256 = self.get_bitmap_hash(self.vm_a) | |
166 | ||
d8130f4c VSO |
167 | if pre_shutdown: |
168 | self.vm_a.shutdown() | |
169 | self.vm_a.launch() | |
170 | ||
171 | result = self.vm_a.qmp('migrate-set-capabilities', | |
172 | capabilities=mig_caps) | |
173 | self.assert_qmp(result, 'return', {}) | |
174 | ||
25bf2426 | 175 | result = self.vm_a.qmp('migrate', uri=mig_cmd) |
33dac6f3 VSO |
176 | while True: |
177 | event = self.vm_a.event_wait('MIGRATION') | |
178 | if event['data']['status'] == 'completed': | |
179 | break | |
180 | ||
181 | if not online: | |
182 | self.vm_a.shutdown() | |
183 | self.vm_b.launch() | |
25bf2426 VSO |
184 | result = self.vm_b.qmp('migrate-set-capabilities', |
185 | capabilities=mig_caps) | |
186 | self.assert_qmp(result, 'return', {}) | |
187 | result = self.vm_b.qmp('migrate-incoming', uri=incoming_cmd) | |
188 | self.assert_qmp(result, 'return', {}) | |
33dac6f3 | 189 | |
25bf2426 VSO |
190 | while True: |
191 | event = self.vm_b.event_wait('MIGRATION') | |
192 | if event['data']['status'] == 'completed': | |
193 | break | |
33dac6f3 VSO |
194 | |
195 | self.check_bitmap(self.vm_b, sha256 if should_migrate else False) | |
196 | ||
197 | if should_migrate: | |
198 | self.vm_b.shutdown() | |
b9247fc1 VSO |
199 | |
200 | # catch 'Could not reopen qcow2 layer: Bitmap already exists' | |
201 | # possible error | |
202 | log = self.vm_b.get_log() | |
203 | log = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', log) | |
204 | log = re.sub(r'\[I \+\d+\.\d+\] CLOSED\n?$', '', log) | |
205 | self.assertEqual(log, '') | |
206 | ||
25bf2426 VSO |
207 | # recreate vm_b, as we don't want -incoming option (this will lead |
208 | # to "cat" process left alive after test finish) | |
209 | self.vm_b = iotests.VM(path_suffix='b') | |
210 | self.vm_b.add_drive(disk_a if shared_storage else disk_b) | |
33dac6f3 VSO |
211 | self.vm_b.launch() |
212 | self.check_bitmap(self.vm_b, sha256 if persistent else False) | |
213 | ||
214 | ||
215 | def inject_test_case(klass, name, method, *args, **kwargs): | |
216 | mc = operator.methodcaller(method, *args, **kwargs) | |
c1a65cba | 217 | setattr(klass, 'test_' + method + name, lambda self: mc(self)) |
33dac6f3 | 218 | |
d8130f4c | 219 | for cmb in list(itertools.product((True, False), repeat=5)): |
33dac6f3 VSO |
220 | name = ('_' if cmb[0] else '_not_') + 'persistent_' |
221 | name += ('_' if cmb[1] else '_not_') + 'migbitmap_' | |
222 | name += '_online' if cmb[2] else '_offline' | |
f7640f0d | 223 | name += '_shared' if cmb[3] else '_nonshared' |
d8130f4c VSO |
224 | if (cmb[4]): |
225 | name += '__pre_shutdown' | |
33dac6f3 VSO |
226 | |
227 | inject_test_case(TestDirtyBitmapMigration, name, 'do_test_migration', | |
f7640f0d | 228 | *list(cmb)) |
33dac6f3 | 229 | |
3e6d88f2 VSO |
230 | for cmb in list(itertools.product((True, False), repeat=2)): |
231 | name = ('_' if cmb[0] else '_not_') + 'persistent_' | |
232 | name += ('_' if cmb[1] else '_not_') + 'migbitmap' | |
233 | ||
234 | inject_test_case(TestDirtyBitmapMigration, name, | |
235 | 'do_test_migration_resume_source', *list(cmb)) | |
33dac6f3 VSO |
236 | |
237 | if __name__ == '__main__': | |
103cbc77 HR |
238 | iotests.main(supported_fmts=['qcow2'], |
239 | supported_protocols=['file']) |