mirror of https://github.com/xemu-project/xemu.git
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)
|
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):
|
class TestIncrementalBackup(iotests.QMPTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.bitmaps = list()
|
self.bitmaps = list()
|
||||||
|
@ -73,6 +118,128 @@ class TestIncrementalBackup(iotests.QMPTestCase):
|
||||||
iotests.qemu_img('create', '-f', fmt, img, size)
|
iotests.qemu_img('create', '-f', fmt, img, size)
|
||||||
self.files.append(img)
|
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):
|
def test_sync_dirty_bitmap_missing(self):
|
||||||
self.assert_no_active_block_jobs()
|
self.assert_no_active_block_jobs()
|
||||||
self.files.append(self.err_img)
|
self.files.append(self.err_img)
|
||||||
|
@ -93,11 +260,10 @@ class TestIncrementalBackup(iotests.QMPTestCase):
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
self.vm.shutdown()
|
self.vm.shutdown()
|
||||||
|
for bitmap in self.bitmaps:
|
||||||
|
bitmap.cleanup()
|
||||||
for filename in self.files:
|
for filename in self.files:
|
||||||
try:
|
try_remove(filename)
|
||||||
os.remove(filename)
|
|
||||||
except OSError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
..
|
...
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
Ran 2 tests
|
Ran 3 tests
|
||||||
|
|
||||||
OK
|
OK
|
||||||
|
|
Loading…
Reference in New Issue