]>
Commit | Line | Data |
---|---|---|
33dac6f3 VSO |
1 | #!/usr/bin/env python |
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 VSO |
136 | def do_test_migration(self, persistent, migrate_bitmaps, online, |
137 | shared_storage): | |
138 | granularity = 512 | |
139 | ||
140 | # regions = ((start, count), ...) | |
141 | regions = ((0, 0x10000), | |
142 | (0xf0000, 0x10000), | |
143 | (0xa0201, 0x1000)) | |
144 | ||
145 | should_migrate = migrate_bitmaps or persistent and shared_storage | |
25bf2426 VSO |
146 | mig_caps = [{'capability': 'events', 'state': True}] |
147 | if migrate_bitmaps: | |
148 | mig_caps.append({'capability': 'dirty-bitmaps', 'state': True}) | |
33dac6f3 | 149 | |
25bf2426 VSO |
150 | result = self.vm_a.qmp('migrate-set-capabilities', |
151 | capabilities=mig_caps) | |
152 | self.assert_qmp(result, 'return', {}) | |
153 | ||
154 | self.vm_b.add_incoming(incoming_cmd if online else "defer") | |
33dac6f3 VSO |
155 | self.vm_b.add_drive(disk_a if shared_storage else disk_b) |
156 | ||
157 | if online: | |
158 | os.mkfifo(mig_file) | |
159 | self.vm_b.launch() | |
25bf2426 VSO |
160 | result = self.vm_b.qmp('migrate-set-capabilities', |
161 | capabilities=mig_caps) | |
162 | self.assert_qmp(result, 'return', {}) | |
33dac6f3 VSO |
163 | |
164 | self.add_bitmap(self.vm_a, granularity, persistent) | |
165 | for r in regions: | |
166 | self.vm_a.hmp_qemu_io('drive0', 'write %d %d' % r) | |
167 | sha256 = self.get_bitmap_hash(self.vm_a) | |
168 | ||
25bf2426 | 169 | result = self.vm_a.qmp('migrate', uri=mig_cmd) |
33dac6f3 VSO |
170 | while True: |
171 | event = self.vm_a.event_wait('MIGRATION') | |
172 | if event['data']['status'] == 'completed': | |
173 | break | |
174 | ||
175 | if not online: | |
176 | self.vm_a.shutdown() | |
177 | self.vm_b.launch() | |
25bf2426 VSO |
178 | result = self.vm_b.qmp('migrate-set-capabilities', |
179 | capabilities=mig_caps) | |
180 | self.assert_qmp(result, 'return', {}) | |
181 | result = self.vm_b.qmp('migrate-incoming', uri=incoming_cmd) | |
182 | self.assert_qmp(result, 'return', {}) | |
33dac6f3 | 183 | |
25bf2426 VSO |
184 | while True: |
185 | event = self.vm_b.event_wait('MIGRATION') | |
186 | if event['data']['status'] == 'completed': | |
187 | break | |
33dac6f3 VSO |
188 | |
189 | self.check_bitmap(self.vm_b, sha256 if should_migrate else False) | |
190 | ||
191 | if should_migrate: | |
192 | self.vm_b.shutdown() | |
b9247fc1 VSO |
193 | |
194 | # catch 'Could not reopen qcow2 layer: Bitmap already exists' | |
195 | # possible error | |
196 | log = self.vm_b.get_log() | |
197 | log = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', log) | |
198 | log = re.sub(r'\[I \+\d+\.\d+\] CLOSED\n?$', '', log) | |
199 | self.assertEqual(log, '') | |
200 | ||
25bf2426 VSO |
201 | # recreate vm_b, as we don't want -incoming option (this will lead |
202 | # to "cat" process left alive after test finish) | |
203 | self.vm_b = iotests.VM(path_suffix='b') | |
204 | self.vm_b.add_drive(disk_a if shared_storage else disk_b) | |
33dac6f3 VSO |
205 | self.vm_b.launch() |
206 | self.check_bitmap(self.vm_b, sha256 if persistent else False) | |
207 | ||
208 | ||
209 | def inject_test_case(klass, name, method, *args, **kwargs): | |
210 | mc = operator.methodcaller(method, *args, **kwargs) | |
c1a65cba | 211 | setattr(klass, 'test_' + method + name, lambda self: mc(self)) |
33dac6f3 | 212 | |
f7640f0d | 213 | for cmb in list(itertools.product((True, False), repeat=4)): |
33dac6f3 VSO |
214 | name = ('_' if cmb[0] else '_not_') + 'persistent_' |
215 | name += ('_' if cmb[1] else '_not_') + 'migbitmap_' | |
216 | name += '_online' if cmb[2] else '_offline' | |
f7640f0d | 217 | name += '_shared' if cmb[3] else '_nonshared' |
33dac6f3 VSO |
218 | |
219 | inject_test_case(TestDirtyBitmapMigration, name, 'do_test_migration', | |
f7640f0d | 220 | *list(cmb)) |
33dac6f3 | 221 | |
3e6d88f2 VSO |
222 | for cmb in list(itertools.product((True, False), repeat=2)): |
223 | name = ('_' if cmb[0] else '_not_') + 'persistent_' | |
224 | name += ('_' if cmb[1] else '_not_') + 'migbitmap' | |
225 | ||
226 | inject_test_case(TestDirtyBitmapMigration, name, | |
227 | 'do_test_migration_resume_source', *list(cmb)) | |
33dac6f3 VSO |
228 | |
229 | if __name__ == '__main__': | |
230 | iotests.main(supported_fmts=['qcow2']) |