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:
parent
7898f74e78
commit
a3d715958c
@ -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__':
|
||||
|
@ -1,5 +1,5 @@
|
||||
..
|
||||
...
|
||||
----------------------------------------------------------------------
|
||||
Ran 2 tests
|
||||
Ran 3 tests
|
||||
|
||||
OK
|
||||
|
Loading…
Reference in New Issue
Block a user