]>
git.proxmox.com Git - mirror_qemu.git/blob - tests/qemu-iotests/124
3 # Tests for incremental drive-backup
5 # Copyright (C) 2015 John Snow for 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 def io_write_patterns(img
, patterns
):
28 for pattern
in patterns
:
29 iotests
.qemu_io('-c', 'write -P%s %s %s' % pattern
, img
)
40 def __init__(self
, name
, drive
):
46 def base_target(self
):
47 return (self
.drive
['backup'], None)
49 def new_target(self
, num
=None):
53 base
= os
.path
.join(iotests
.test_dir
,
54 "%s.%s." % (self
.drive
['id'], self
.name
))
55 suff
= "%i.%s" % (num
, self
.drive
['fmt'])
56 target
= base
+ "inc" + suff
57 reference
= base
+ "ref" + suff
58 self
.backups
.append((target
, reference
))
59 return (target
, reference
)
61 def last_target(self
):
63 return self
.backups
[-1]
64 return self
.base_target()
67 for image
in self
.backups
.pop():
72 for backup
in self
.backups
:
77 class TestIncrementalBackup(iotests
.QMPTestCase
):
82 self
.vm
= iotests
.VM()
83 self
.err_img
= os
.path
.join(iotests
.test_dir
, 'err.%s' % iotests
.imgfmt
)
85 # Create a base image with a distinctive patterning
86 drive0
= self
.add_node('drive0')
87 self
.img_create(drive0
['file'], drive0
['fmt'])
88 self
.vm
.add_drive(drive0
['file'])
89 io_write_patterns(drive0
['file'], (('0x41', 0, 512),
90 ('0xd5', '1M', '32k'),
91 ('0xdc', '32M', '124k')))
95 def add_node(self
, node_id
, fmt
=iotests
.imgfmt
, path
=None, backup
=None):
97 path
= os
.path
.join(iotests
.test_dir
, '%s.%s' % (node_id
, fmt
))
99 backup
= os
.path
.join(iotests
.test_dir
,
100 '%s.full.backup.%s' % (node_id
, fmt
))
107 return self
.drives
[-1]
110 def img_create(self
, img
, fmt
=iotests
.imgfmt
, size
='64M',
111 parent
=None, parentFormat
=None):
113 if parentFormat
is None:
115 iotests
.qemu_img('create', '-f', fmt
, img
, size
,
116 '-b', parent
, '-F', parentFormat
)
118 iotests
.qemu_img('create', '-f', fmt
, img
, size
)
119 self
.files
.append(img
)
122 def do_qmp_backup(self
, error
='Input/output error', **kwargs
):
123 res
= self
.vm
.qmp('drive-backup', **kwargs
)
124 self
.assert_qmp(res
, 'return', {})
126 event
= self
.vm
.event_wait(name
="BLOCK_JOB_COMPLETED",
127 match
={'data': {'device': kwargs
['device']}})
128 self
.assertIsNotNone(event
)
131 failure
= self
.dictpath(event
, 'data/error')
132 except AssertionError:
134 self
.assert_qmp(event
, 'data/offset', event
['data']['len'])
138 self
.assert_qmp(event
, 'data/error', error
)
142 def create_anchor_backup(self
, drive
=None):
144 drive
= self
.drives
[-1]
145 res
= self
.do_qmp_backup(device
=drive
['id'], sync
='full',
146 format
=drive
['fmt'], target
=drive
['backup'])
148 self
.files
.append(drive
['backup'])
149 return drive
['backup']
152 def make_reference_backup(self
, bitmap
=None):
154 bitmap
= self
.bitmaps
[-1]
155 _
, reference
= bitmap
.last_target()
156 res
= self
.do_qmp_backup(device
=bitmap
.drive
['id'], sync
='full',
157 format
=bitmap
.drive
['fmt'], target
=reference
)
161 def add_bitmap(self
, name
, drive
):
162 bitmap
= Bitmap(name
, drive
)
163 self
.bitmaps
.append(bitmap
)
164 result
= self
.vm
.qmp('block-dirty-bitmap-add', node
=drive
['id'],
166 self
.assert_qmp(result
, 'return', {})
170 def prepare_backup(self
, bitmap
=None, parent
=None):
172 bitmap
= self
.bitmaps
[-1]
174 parent
, _
= bitmap
.last_target()
176 target
, _
= bitmap
.new_target()
177 self
.img_create(target
, bitmap
.drive
['fmt'], parent
=parent
)
181 def create_incremental(self
, bitmap
=None, parent
=None,
182 parentFormat
=None, validate
=True):
184 bitmap
= self
.bitmaps
[-1]
186 parent
, _
= bitmap
.last_target()
188 target
= self
.prepare_backup(bitmap
, parent
)
189 res
= self
.do_qmp_backup(device
=bitmap
.drive
['id'],
190 sync
='dirty-bitmap', bitmap
=bitmap
.name
,
191 format
=bitmap
.drive
['fmt'], target
=target
,
195 self
.assertFalse(validate
)
197 self
.make_reference_backup(bitmap
)
201 def check_backups(self
):
202 for bitmap
in self
.bitmaps
:
203 for incremental
, reference
in bitmap
.backups
:
204 self
.assertTrue(iotests
.compare_images(incremental
, reference
))
205 last
= bitmap
.last_target()[0]
206 self
.assertTrue(iotests
.compare_images(last
, bitmap
.drive
['file']))
209 def hmp_io_writes(self
, drive
, patterns
):
210 for pattern
in patterns
:
211 self
.vm
.hmp_qemu_io(drive
, 'write -P%s %s %s' % pattern
)
212 self
.vm
.hmp_qemu_io(drive
, 'flush')
215 def test_incremental_simple(self
):
217 Test: Create and verify three incremental backups.
219 Create a bitmap and a full backup before VM execution begins,
220 then create a series of three incremental backups "during execution,"
221 i.e.; after IO requests begin modifying the drive.
223 self
.create_anchor_backup()
224 self
.add_bitmap('bitmap0', self
.drives
[0])
226 # Sanity: Create a "hollow" incremental backup
227 self
.create_incremental()
228 # Three writes: One complete overwrite, one new segment,
229 # and one partial overlap.
230 self
.hmp_io_writes(self
.drives
[0]['id'], (('0xab', 0, 512),
231 ('0xfe', '16M', '256k'),
232 ('0x64', '32736k', '64k')))
233 self
.create_incremental()
234 # Three more writes, one of each kind, like above
235 self
.hmp_io_writes(self
.drives
[0]['id'], (('0x9a', 0, 512),
236 ('0x55', '8M', '352k'),
237 ('0x78', '15872k', '1M')))
238 self
.create_incremental()
243 def test_sync_dirty_bitmap_missing(self
):
244 self
.assert_no_active_block_jobs()
245 self
.files
.append(self
.err_img
)
246 result
= self
.vm
.qmp('drive-backup', device
=self
.drives
[0]['id'],
247 sync
='dirty-bitmap', format
=self
.drives
[0]['fmt'],
249 self
.assert_qmp(result
, 'error/class', 'GenericError')
252 def test_sync_dirty_bitmap_not_found(self
):
253 self
.assert_no_active_block_jobs()
254 self
.files
.append(self
.err_img
)
255 result
= self
.vm
.qmp('drive-backup', device
=self
.drives
[0]['id'],
256 sync
='dirty-bitmap', bitmap
='unknown',
257 format
=self
.drives
[0]['fmt'], target
=self
.err_img
)
258 self
.assert_qmp(result
, 'error/class', 'GenericError')
263 for bitmap
in self
.bitmaps
:
265 for filename
in self
.files
:
269 if __name__
== '__main__':
270 iotests
.main(supported_fmts
=['qcow2'])