qemu/tests/qemu-iotests/056
Max Reitz 68474776f3 iotests: Different iterator behavior in Python 3
In Python 3, several functions now return iterators instead of lists.
This includes range(), items(), map(), and filter().  This means that if
we really want a list, we have to wrap those instances with list().  But
then again, the two instances where this is the case for map() and
filter(), there are shorter expressions which work without either
function.

On the other hand, sometimes we do just want an iterator, in which case
we have sometimes used xrange() and iteritems() which no longer exist in
Python 3.  Just change these calls to be range() and items(), works in
both Python 2 and 3, and is really what we want in 3 (which is what
matters).  But because it is so simple to do (and to find and remove
once we completely switch to Python 3), make range() be an alias for
xrange() in the two affected tests (044 and 163).

In one instance, we only wanted the first instance of the result of a
filter() call.  Instead of using next(filter()) which would work only in
Python 3, or list(filter())[0] which would work everywhere but is a bit
weird, this instance is changed to use a generator expression with a
next() wrapped around, which works both in 2.7 and 3.

Signed-off-by: Max Reitz <mreitz@redhat.com>
Reviewed-by: Eduardo Habkost <ehabkost@redhat.com>
Reviewed-by: Cleber Rosa <crosa@redhat.com>
Message-Id: <20181022135307.14398-6-mreitz@redhat.com>
Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
2018-10-30 21:11:52 -03:00

300 lines
12 KiB
Python
Executable File

#!/usr/bin/env python
#
# Tests for drive-backup
#
# Copyright (C) 2013 Red Hat, Inc.
#
# Based on 041.
#
# 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 <http://www.gnu.org/licenses/>.
#
import time
import os
import iotests
from iotests import qemu_img, qemu_io, create_image
backing_img = os.path.join(iotests.test_dir, 'backing.img')
test_img = os.path.join(iotests.test_dir, 'test.img')
target_img = os.path.join(iotests.test_dir, 'target.img')
def img_create(img, fmt=iotests.imgfmt, size='64M', **kwargs):
fullname = os.path.join(iotests.test_dir, '%s.%s' % (img, fmt))
optargs = []
for k,v in kwargs.items():
optargs = optargs + ['-o', '%s=%s' % (k,v)]
args = ['create', '-f', fmt] + optargs + [fullname, size]
iotests.qemu_img(*args)
return fullname
def try_remove(img):
try:
os.remove(img)
except OSError:
pass
def io_write_patterns(img, patterns):
for pattern in patterns:
iotests.qemu_io('-c', 'write -P%s %s %s' % pattern, img)
class TestSyncModesNoneAndTop(iotests.QMPTestCase):
image_len = 64 * 1024 * 1024 # MB
def setUp(self):
create_image(backing_img, TestSyncModesNoneAndTop.image_len)
qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img)
qemu_io('-c', 'write -P0x41 0 512', test_img)
qemu_io('-c', 'write -P0xd5 1M 32k', test_img)
qemu_io('-c', 'write -P0xdc 32M 124k', test_img)
qemu_io('-c', 'write -P0xdc 67043328 64k', test_img)
self.vm = iotests.VM().add_drive(test_img)
self.vm.launch()
def tearDown(self):
self.vm.shutdown()
os.remove(test_img)
os.remove(backing_img)
try:
os.remove(target_img)
except OSError:
pass
def test_complete_top(self):
self.assert_no_active_block_jobs()
result = self.vm.qmp('drive-backup', device='drive0', sync='top',
format=iotests.imgfmt, target=target_img)
self.assert_qmp(result, 'return', {})
self.wait_until_completed(check_offset=False)
self.assert_no_active_block_jobs()
self.vm.shutdown()
self.assertTrue(iotests.compare_images(test_img, target_img),
'target image does not match source after backup')
def test_cancel_sync_none(self):
self.assert_no_active_block_jobs()
result = self.vm.qmp('drive-backup', device='drive0',
sync='none', target=target_img)
self.assert_qmp(result, 'return', {})
time.sleep(1)
self.vm.hmp_qemu_io('drive0', 'write -P0x5e 0 512')
self.vm.hmp_qemu_io('drive0', 'aio_flush')
# Verify that the original contents exist in the target image.
event = self.cancel_and_wait()
self.assert_qmp(event, 'data/type', 'backup')
self.vm.shutdown()
time.sleep(1)
self.assertEqual(-1, qemu_io('-c', 'read -P0x41 0 512', target_img).find("verification failed"))
class TestBeforeWriteNotifier(iotests.QMPTestCase):
def setUp(self):
self.vm = iotests.VM().add_drive_raw("file=blkdebug::null-co://,id=drive0,align=65536,driver=blkdebug")
self.vm.launch()
def tearDown(self):
self.vm.shutdown()
os.remove(target_img)
def test_before_write_notifier(self):
self.vm.pause_drive("drive0")
result = self.vm.qmp('drive-backup', device='drive0',
sync='full', target=target_img,
format="file", speed=1)
self.assert_qmp(result, 'return', {})
result = self.vm.qmp('block-job-pause', device="drive0")
self.assert_qmp(result, 'return', {})
# Speed is low enough that this must be an uncopied range, which will
# trigger the before write notifier
self.vm.hmp_qemu_io('drive0', 'aio_write -P 1 512512 512')
self.vm.resume_drive("drive0")
result = self.vm.qmp('block-job-resume', device="drive0")
self.assert_qmp(result, 'return', {})
event = self.cancel_and_wait()
self.assert_qmp(event, 'data/type', 'backup')
class BackupTest(iotests.QMPTestCase):
def setUp(self):
self.vm = iotests.VM()
self.test_img = img_create('test')
self.dest_img = img_create('dest')
self.vm.add_drive(self.test_img)
self.vm.launch()
def tearDown(self):
self.vm.shutdown()
try_remove(self.test_img)
try_remove(self.dest_img)
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 qmp_backup_and_wait(self, cmd='drive-backup', serror=None,
aerror=None, **kwargs):
if not self.qmp_backup(cmd, serror, **kwargs):
return False
return self.qmp_backup_wait(kwargs['device'], aerror)
def qmp_backup(self, cmd='drive-backup',
error=None, **kwargs):
self.assertTrue('device' in kwargs)
res = self.vm.qmp(cmd, **kwargs)
if error:
self.assert_qmp(res, 'error/desc', error)
return False
self.assert_qmp(res, 'return', {})
return True
def qmp_backup_wait(self, device, error=None):
event = self.vm.event_wait(name="BLOCK_JOB_COMPLETED",
match={'data': {'device': 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:
# Failure.
self.assert_qmp(event, 'data/error', qerror)
return False
def test_dismiss_false(self):
res = self.vm.qmp('query-block-jobs')
self.assert_qmp(res, 'return', [])
self.qmp_backup_and_wait(device='drive0', format=iotests.imgfmt,
sync='full', target=self.dest_img,
auto_dismiss=True)
res = self.vm.qmp('query-block-jobs')
self.assert_qmp(res, 'return', [])
def test_dismiss_true(self):
res = self.vm.qmp('query-block-jobs')
self.assert_qmp(res, 'return', [])
self.qmp_backup_and_wait(device='drive0', format=iotests.imgfmt,
sync='full', target=self.dest_img,
auto_dismiss=False)
res = self.vm.qmp('query-block-jobs')
self.assert_qmp(res, 'return[0]/status', 'concluded')
res = self.vm.qmp('block-job-dismiss', id='drive0')
self.assert_qmp(res, 'return', {})
res = self.vm.qmp('query-block-jobs')
self.assert_qmp(res, 'return', [])
def test_dismiss_bad_id(self):
res = self.vm.qmp('query-block-jobs')
self.assert_qmp(res, 'return', [])
res = self.vm.qmp('block-job-dismiss', id='foobar')
self.assert_qmp(res, 'error/class', 'DeviceNotActive')
def test_dismiss_collision(self):
res = self.vm.qmp('query-block-jobs')
self.assert_qmp(res, 'return', [])
self.qmp_backup_and_wait(device='drive0', format=iotests.imgfmt,
sync='full', target=self.dest_img,
auto_dismiss=False)
res = self.vm.qmp('query-block-jobs')
self.assert_qmp(res, 'return[0]/status', 'concluded')
# Leave zombie job un-dismissed, observe a failure:
res = self.qmp_backup_and_wait(serror='Need a root block node',
device='drive0', format=iotests.imgfmt,
sync='full', target=self.dest_img,
auto_dismiss=False)
self.assertEqual(res, False)
# OK, dismiss the zombie.
res = self.vm.qmp('block-job-dismiss', id='drive0')
self.assert_qmp(res, 'return', {})
res = self.vm.qmp('query-block-jobs')
self.assert_qmp(res, 'return', [])
# Ensure it's really gone.
self.qmp_backup_and_wait(device='drive0', format=iotests.imgfmt,
sync='full', target=self.dest_img,
auto_dismiss=False)
def dismissal_failure(self, dismissal_opt):
res = self.vm.qmp('query-block-jobs')
self.assert_qmp(res, 'return', [])
# Give blkdebug something to chew on
self.hmp_io_writes('drive0',
(('0x9a', 0, 512),
('0x55', '8M', '352k'),
('0x78', '15872k', '1M')))
# Add destination node via blkdebug
res = self.vm.qmp('blockdev-add',
node_name='target0',
driver=iotests.imgfmt,
file={
'driver': 'blkdebug',
'image': {
'driver': 'file',
'filename': self.dest_img
},
'inject-error': [{
'event': 'write_aio',
'errno': 5,
'immediately': False,
'once': True
}],
})
self.assert_qmp(res, 'return', {})
res = self.qmp_backup(cmd='blockdev-backup',
device='drive0', target='target0',
on_target_error='stop',
sync='full',
auto_dismiss=dismissal_opt)
self.assertTrue(res)
event = self.vm.event_wait(name="BLOCK_JOB_ERROR",
match={'data': {'device': 'drive0'}})
self.assertNotEqual(event, None)
# OK, job should be wedged
res = self.vm.qmp('query-block-jobs')
self.assert_qmp(res, 'return[0]/status', 'paused')
res = self.vm.qmp('block-job-dismiss', id='drive0')
self.assert_qmp(res, 'error/desc',
"Job 'drive0' in state 'paused' cannot accept"
" command verb 'dismiss'")
res = self.vm.qmp('query-block-jobs')
self.assert_qmp(res, 'return[0]/status', 'paused')
# OK, unstick job and move forward.
res = self.vm.qmp('block-job-resume', device='drive0')
self.assert_qmp(res, 'return', {})
# And now we need to wait for it to conclude;
res = self.qmp_backup_wait(device='drive0')
self.assertTrue(res)
if not dismissal_opt:
# Job should now be languishing:
res = self.vm.qmp('query-block-jobs')
self.assert_qmp(res, 'return[0]/status', 'concluded')
res = self.vm.qmp('block-job-dismiss', id='drive0')
self.assert_qmp(res, 'return', {})
res = self.vm.qmp('query-block-jobs')
self.assert_qmp(res, 'return', [])
def test_dismiss_premature(self):
self.dismissal_failure(False)
def test_dismiss_erroneous(self):
self.dismissal_failure(True)
if __name__ == '__main__':
iotests.main(supported_fmts=['qcow2', 'qed'])