#!/usr/bin/env python # # Tests for incremental drive-backup # # Copyright (C) 2015 John Snow for Red Hat, Inc. # # Based on 056. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # import os import iotests def io_write_patterns(img, patterns): for pattern in 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() self.files = list() self.drives = list() self.vm = iotests.VM() self.err_img = os.path.join(iotests.test_dir, 'err.%s' % iotests.imgfmt) # Create a base image with a distinctive patterning drive0 = self.add_node('drive0') self.img_create(drive0['file'], drive0['fmt']) self.vm.add_drive(drive0['file']) io_write_patterns(drive0['file'], (('0x41', 0, 512), ('0xd5', '1M', '32k'), ('0xdc', '32M', '124k'))) self.vm.launch() def add_node(self, node_id, fmt=iotests.imgfmt, path=None, backup=None): if path is None: path = os.path.join(iotests.test_dir, '%s.%s' % (node_id, fmt)) if backup is None: backup = os.path.join(iotests.test_dir, '%s.full.backup.%s' % (node_id, fmt)) self.drives.append({ 'id': node_id, 'file': path, 'backup': backup, 'fmt': fmt }) return self.drives[-1] def img_create(self, img, fmt=iotests.imgfmt, size='64M', parent=None, parentFormat=None): if parent: if parentFormat is None: parentFormat = fmt iotests.qemu_img('create', '-f', fmt, img, size, '-b', parent, '-F', parentFormat) else: 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.assertNotEqual(event, None) 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)