iotests: add simple incremental backup case

Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Message-id: 1429314609-29776-20-git-send-email-jsnow@redhat.com
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
John Snow 2015-04-17 19:50:07 -04:00 committed by Kevin Wolf
parent 7898f74e78
commit a3d715958c
2 changed files with 172 additions and 6 deletions

View File

@ -29,6 +29,51 @@ def io_write_patterns(img, patterns):
iotests.qemu_io('-c', 'write -P%s %s %s' % pattern, img)
def try_remove(img):
try:
os.remove(img)
except OSError:
pass
class Bitmap:
def __init__(self, name, drive):
self.name = name
self.drive = drive
self.num = 0
self.backups = list()
def base_target(self):
return (self.drive['backup'], None)
def new_target(self, num=None):
if num is None:
num = self.num
self.num = num + 1
base = os.path.join(iotests.test_dir,
"%s.%s." % (self.drive['id'], self.name))
suff = "%i.%s" % (num, self.drive['fmt'])
target = base + "inc" + suff
reference = base + "ref" + suff
self.backups.append((target, reference))
return (target, reference)
def last_target(self):
if self.backups:
return self.backups[-1]
return self.base_target()
def del_target(self):
for image in self.backups.pop():
try_remove(image)
self.num -= 1
def cleanup(self):
for backup in self.backups:
for image in backup:
try_remove(image)
class TestIncrementalBackup(iotests.QMPTestCase):
def setUp(self):
self.bitmaps = list()
@ -73,6 +118,128 @@ class TestIncrementalBackup(iotests.QMPTestCase):
iotests.qemu_img('create', '-f', fmt, img, size)
self.files.append(img)
def do_qmp_backup(self, error='Input/output error', **kwargs):
res = self.vm.qmp('drive-backup', **kwargs)
self.assert_qmp(res, 'return', {})
event = self.vm.event_wait(name="BLOCK_JOB_COMPLETED",
match={'data': {'device': kwargs['device']}})
self.assertIsNotNone(event)
try:
failure = self.dictpath(event, 'data/error')
except AssertionError:
# Backup succeeded.
self.assert_qmp(event, 'data/offset', event['data']['len'])
return True
else:
# Backup failed.
self.assert_qmp(event, 'data/error', error)
return False
def create_anchor_backup(self, drive=None):
if drive is None:
drive = self.drives[-1]
res = self.do_qmp_backup(device=drive['id'], sync='full',
format=drive['fmt'], target=drive['backup'])
self.assertTrue(res)
self.files.append(drive['backup'])
return drive['backup']
def make_reference_backup(self, bitmap=None):
if bitmap is None:
bitmap = self.bitmaps[-1]
_, reference = bitmap.last_target()
res = self.do_qmp_backup(device=bitmap.drive['id'], sync='full',
format=bitmap.drive['fmt'], target=reference)
self.assertTrue(res)
def add_bitmap(self, name, drive):
bitmap = Bitmap(name, drive)
self.bitmaps.append(bitmap)
result = self.vm.qmp('block-dirty-bitmap-add', node=drive['id'],
name=bitmap.name)
self.assert_qmp(result, 'return', {})
return bitmap
def prepare_backup(self, bitmap=None, parent=None):
if bitmap is None:
bitmap = self.bitmaps[-1]
if parent is None:
parent, _ = bitmap.last_target()
target, _ = bitmap.new_target()
self.img_create(target, bitmap.drive['fmt'], parent=parent)
return target
def create_incremental(self, bitmap=None, parent=None,
parentFormat=None, validate=True):
if bitmap is None:
bitmap = self.bitmaps[-1]
if parent is None:
parent, _ = bitmap.last_target()
target = self.prepare_backup(bitmap, parent)
res = self.do_qmp_backup(device=bitmap.drive['id'],
sync='dirty-bitmap', bitmap=bitmap.name,
format=bitmap.drive['fmt'], target=target,
mode='existing')
if not res:
bitmap.del_target();
self.assertFalse(validate)
else:
self.make_reference_backup(bitmap)
return res
def check_backups(self):
for bitmap in self.bitmaps:
for incremental, reference in bitmap.backups:
self.assertTrue(iotests.compare_images(incremental, reference))
last = bitmap.last_target()[0]
self.assertTrue(iotests.compare_images(last, bitmap.drive['file']))
def hmp_io_writes(self, drive, patterns):
for pattern in patterns:
self.vm.hmp_qemu_io(drive, 'write -P%s %s %s' % pattern)
self.vm.hmp_qemu_io(drive, 'flush')
def test_incremental_simple(self):
'''
Test: Create and verify three incremental backups.
Create a bitmap and a full backup before VM execution begins,
then create a series of three incremental backups "during execution,"
i.e.; after IO requests begin modifying the drive.
'''
self.create_anchor_backup()
self.add_bitmap('bitmap0', self.drives[0])
# Sanity: Create a "hollow" incremental backup
self.create_incremental()
# Three writes: One complete overwrite, one new segment,
# and one partial overlap.
self.hmp_io_writes(self.drives[0]['id'], (('0xab', 0, 512),
('0xfe', '16M', '256k'),
('0x64', '32736k', '64k')))
self.create_incremental()
# Three more writes, one of each kind, like above
self.hmp_io_writes(self.drives[0]['id'], (('0x9a', 0, 512),
('0x55', '8M', '352k'),
('0x78', '15872k', '1M')))
self.create_incremental()
self.vm.shutdown()
self.check_backups()
def test_sync_dirty_bitmap_missing(self):
self.assert_no_active_block_jobs()
self.files.append(self.err_img)
@ -93,11 +260,10 @@ class TestIncrementalBackup(iotests.QMPTestCase):
def tearDown(self):
self.vm.shutdown()
for bitmap in self.bitmaps:
bitmap.cleanup()
for filename in self.files:
try:
os.remove(filename)
except OSError:
pass
try_remove(filename)
if __name__ == '__main__':

View File

@ -1,5 +1,5 @@
..
...
----------------------------------------------------------------------
Ran 2 tests
Ran 3 tests
OK