2020-01-30 17:32:23 +01:00
|
|
|
#!/usr/bin/env python3
|
2012-10-23 16:39:22 +02:00
|
|
|
#
|
|
|
|
# Tests for image mirroring.
|
|
|
|
#
|
|
|
|
# Copyright (C) 2012 Red Hat, Inc.
|
|
|
|
#
|
|
|
|
# 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
|
2020-02-18 11:34:53 +01:00
|
|
|
import re
|
2012-10-23 16:39:22 +02:00
|
|
|
import iotests
|
|
|
|
from iotests import qemu_img, qemu_io
|
|
|
|
|
|
|
|
backing_img = os.path.join(iotests.test_dir, 'backing.img')
|
|
|
|
target_backing_img = os.path.join(iotests.test_dir, 'target-backing.img')
|
|
|
|
test_img = os.path.join(iotests.test_dir, 'test.img')
|
|
|
|
target_img = os.path.join(iotests.test_dir, 'target.img')
|
|
|
|
|
2014-06-27 18:25:26 +02:00
|
|
|
quorum_img1 = os.path.join(iotests.test_dir, 'quorum1.img')
|
|
|
|
quorum_img2 = os.path.join(iotests.test_dir, 'quorum2.img')
|
|
|
|
quorum_img3 = os.path.join(iotests.test_dir, 'quorum3.img')
|
|
|
|
quorum_repair_img = os.path.join(iotests.test_dir, 'quorum_repair.img')
|
|
|
|
quorum_snapshot_file = os.path.join(iotests.test_dir, 'quorum_snapshot.img')
|
|
|
|
|
2020-04-24 15:46:26 +02:00
|
|
|
nbd_sock_path = os.path.join(iotests.sock_dir, 'nbd.sock')
|
2020-02-18 11:34:53 +01:00
|
|
|
|
2015-06-08 07:56:12 +02:00
|
|
|
class TestSingleDrive(iotests.QMPTestCase):
|
2012-10-23 16:39:22 +02:00
|
|
|
image_len = 1 * 1024 * 1024 # MB
|
2015-12-24 05:45:06 +01:00
|
|
|
qmp_cmd = 'drive-mirror'
|
|
|
|
qmp_target = target_img
|
2012-10-23 16:39:22 +02:00
|
|
|
|
|
|
|
def setUp(self):
|
2014-06-24 14:26:38 +02:00
|
|
|
iotests.create_image(backing_img, self.image_len)
|
iotests: Specify explicit backing format where sensible
There are many existing qcow2 images that specify a backing file but
no format. This has been the source of CVEs in the past, but has
become more prominent of a problem now that libvirt has switched to
-blockdev. With older -drive, at least the probing was always done by
qemu (so the only risk of a changed format between successive boots of
a guest was if qemu was upgraded and probed differently). But with
newer -blockdev, libvirt must specify a format; if libvirt guesses raw
where the image was formatted, this results in data corruption visible
to the guest; conversely, if libvirt guesses qcow2 where qemu was
using raw, this can result in potential security holes, so modern
libvirt instead refuses to use images without explicit backing format.
The change in libvirt to reject images without explicit backing format
has pointed out that a number of tools have been far too reliant on
probing in the past. It's time to set a better example in our own
iotests of properly setting this parameter.
iotest calls to create, rebase, and convert are all impacted to some
degree. It's a bit annoying that we are inconsistent on command line
- while all of those accept -o backing_file=...,backing_fmt=..., the
shortcuts are different: create and rebase have -b and -F, while
convert has -B but no -F. (amend has no shortcuts, but the previous
patch just deprecated the use of amend to change backing chains).
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20200706203954.341758-9-eblake@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2020-07-06 22:39:52 +02:00
|
|
|
qemu_img('create', '-f', iotests.imgfmt,
|
|
|
|
'-o', 'backing_file=%s' % backing_img, '-F', 'raw', test_img)
|
2017-07-18 17:24:05 +02:00
|
|
|
self.vm = iotests.VM().add_drive(test_img, "node-name=top,backing.node-name=base")
|
2015-09-02 20:52:26 +02:00
|
|
|
if iotests.qemu_default_machine == 'pc':
|
|
|
|
self.vm.add_drive(None, 'media=cdrom', 'ide')
|
2012-10-23 16:39:22 +02:00
|
|
|
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(self):
|
2013-05-28 17:11:34 +02:00
|
|
|
self.assert_no_active_block_jobs()
|
2012-10-23 16:39:22 +02:00
|
|
|
|
2015-12-24 05:45:06 +01:00
|
|
|
result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full',
|
|
|
|
target=self.qmp_target)
|
2012-10-23 16:39:22 +02:00
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
|
|
|
self.complete_and_wait()
|
|
|
|
result = self.vm.qmp('query-block')
|
|
|
|
self.assert_qmp(result, 'return[0]/inserted/file', target_img)
|
|
|
|
self.vm.shutdown()
|
2013-05-28 17:11:36 +02:00
|
|
|
self.assertTrue(iotests.compare_images(test_img, target_img),
|
2012-10-23 16:39:22 +02:00
|
|
|
'target image does not match source after mirroring')
|
|
|
|
|
|
|
|
def test_cancel(self):
|
2013-05-28 17:11:34 +02:00
|
|
|
self.assert_no_active_block_jobs()
|
2012-10-23 16:39:22 +02:00
|
|
|
|
2015-12-24 05:45:06 +01:00
|
|
|
result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full',
|
|
|
|
target=self.qmp_target)
|
2012-10-23 16:39:22 +02:00
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
2013-05-28 17:11:35 +02:00
|
|
|
self.cancel_and_wait(force=True)
|
2012-10-23 16:39:22 +02:00
|
|
|
result = self.vm.qmp('query-block')
|
|
|
|
self.assert_qmp(result, 'return[0]/inserted/file', test_img)
|
|
|
|
|
|
|
|
def test_cancel_after_ready(self):
|
2013-05-28 17:11:34 +02:00
|
|
|
self.assert_no_active_block_jobs()
|
2012-10-23 16:39:22 +02:00
|
|
|
|
2015-12-24 05:45:06 +01:00
|
|
|
result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full',
|
|
|
|
target=self.qmp_target)
|
2012-10-23 16:39:22 +02:00
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
2013-05-28 17:11:35 +02:00
|
|
|
self.wait_ready_and_cancel()
|
2012-10-23 16:39:22 +02:00
|
|
|
result = self.vm.qmp('query-block')
|
|
|
|
self.assert_qmp(result, 'return[0]/inserted/file', test_img)
|
|
|
|
self.vm.shutdown()
|
2013-05-28 17:11:36 +02:00
|
|
|
self.assertTrue(iotests.compare_images(test_img, target_img),
|
2012-10-23 16:39:22 +02:00
|
|
|
'target image does not match source after mirroring')
|
|
|
|
|
|
|
|
def test_pause(self):
|
2013-05-28 17:11:34 +02:00
|
|
|
self.assert_no_active_block_jobs()
|
2012-10-23 16:39:22 +02:00
|
|
|
|
2015-12-24 05:45:06 +01:00
|
|
|
result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full',
|
|
|
|
target=self.qmp_target)
|
2012-10-23 16:39:22 +02:00
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
2017-07-21 16:41:21 +02:00
|
|
|
self.pause_job('drive0')
|
2012-10-23 16:39:22 +02:00
|
|
|
|
|
|
|
result = self.vm.qmp('query-block-jobs')
|
|
|
|
offset = self.dictpath(result, 'return[0]/offset')
|
|
|
|
|
2017-07-21 16:41:21 +02:00
|
|
|
time.sleep(0.5)
|
2012-10-23 16:39:22 +02:00
|
|
|
result = self.vm.qmp('query-block-jobs')
|
|
|
|
self.assert_qmp(result, 'return[0]/offset', offset)
|
|
|
|
|
|
|
|
result = self.vm.qmp('block-job-resume', device='drive0')
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
|
|
|
self.complete_and_wait()
|
|
|
|
self.vm.shutdown()
|
2013-05-28 17:11:36 +02:00
|
|
|
self.assertTrue(iotests.compare_images(test_img, target_img),
|
2013-01-22 09:03:13 +01:00
|
|
|
'target image does not match source after mirroring')
|
|
|
|
|
|
|
|
def test_small_buffer(self):
|
2013-05-28 17:11:34 +02:00
|
|
|
self.assert_no_active_block_jobs()
|
2013-01-22 09:03:13 +01:00
|
|
|
|
|
|
|
# A small buffer is rounded up automatically
|
2015-12-24 05:45:06 +01:00
|
|
|
result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full',
|
|
|
|
buf_size=4096, target=self.qmp_target)
|
2013-01-22 09:03:13 +01:00
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
|
|
|
self.complete_and_wait()
|
|
|
|
result = self.vm.qmp('query-block')
|
|
|
|
self.assert_qmp(result, 'return[0]/inserted/file', target_img)
|
|
|
|
self.vm.shutdown()
|
2013-05-28 17:11:36 +02:00
|
|
|
self.assertTrue(iotests.compare_images(test_img, target_img),
|
2013-01-22 09:03:13 +01:00
|
|
|
'target image does not match source after mirroring')
|
|
|
|
|
|
|
|
def test_small_buffer2(self):
|
2013-05-28 17:11:34 +02:00
|
|
|
self.assert_no_active_block_jobs()
|
2013-01-22 09:03:13 +01:00
|
|
|
|
|
|
|
qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,size=%d'
|
2014-06-24 14:26:38 +02:00
|
|
|
% (self.image_len, self.image_len), target_img)
|
2015-12-24 05:45:06 +01:00
|
|
|
result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full',
|
|
|
|
buf_size=65536, mode='existing', target=self.qmp_target)
|
2013-01-22 09:03:13 +01:00
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
|
|
|
self.complete_and_wait()
|
|
|
|
result = self.vm.qmp('query-block')
|
|
|
|
self.assert_qmp(result, 'return[0]/inserted/file', target_img)
|
|
|
|
self.vm.shutdown()
|
2013-05-28 17:11:36 +02:00
|
|
|
self.assertTrue(iotests.compare_images(test_img, target_img),
|
2012-10-23 16:39:22 +02:00
|
|
|
'target image does not match source after mirroring')
|
|
|
|
|
|
|
|
def test_large_cluster(self):
|
2013-05-28 17:11:34 +02:00
|
|
|
self.assert_no_active_block_jobs()
|
2012-10-23 16:39:22 +02:00
|
|
|
|
|
|
|
qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,backing_file=%s'
|
iotests: Specify explicit backing format where sensible
There are many existing qcow2 images that specify a backing file but
no format. This has been the source of CVEs in the past, but has
become more prominent of a problem now that libvirt has switched to
-blockdev. With older -drive, at least the probing was always done by
qemu (so the only risk of a changed format between successive boots of
a guest was if qemu was upgraded and probed differently). But with
newer -blockdev, libvirt must specify a format; if libvirt guesses raw
where the image was formatted, this results in data corruption visible
to the guest; conversely, if libvirt guesses qcow2 where qemu was
using raw, this can result in potential security holes, so modern
libvirt instead refuses to use images without explicit backing format.
The change in libvirt to reject images without explicit backing format
has pointed out that a number of tools have been far too reliant on
probing in the past. It's time to set a better example in our own
iotests of properly setting this parameter.
iotest calls to create, rebase, and convert are all impacted to some
degree. It's a bit annoying that we are inconsistent on command line
- while all of those accept -o backing_file=...,backing_fmt=..., the
shortcuts are different: create and rebase have -b and -F, while
convert has -B but no -F. (amend has no shortcuts, but the previous
patch just deprecated the use of amend to change backing chains).
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20200706203954.341758-9-eblake@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2020-07-06 22:39:52 +02:00
|
|
|
% (self.image_len, backing_img),
|
|
|
|
'-F', 'raw', target_img)
|
2015-12-24 05:45:06 +01:00
|
|
|
result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full',
|
|
|
|
mode='existing', target=self.qmp_target)
|
2012-10-23 16:39:22 +02:00
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
|
|
|
self.complete_and_wait()
|
|
|
|
result = self.vm.qmp('query-block')
|
|
|
|
self.assert_qmp(result, 'return[0]/inserted/file', target_img)
|
|
|
|
self.vm.shutdown()
|
2013-05-28 17:11:36 +02:00
|
|
|
self.assertTrue(iotests.compare_images(test_img, target_img),
|
2012-10-23 16:39:22 +02:00
|
|
|
'target image does not match source after mirroring')
|
|
|
|
|
2017-07-18 17:24:05 +02:00
|
|
|
# Tests that the insertion of the mirror_top filter node doesn't make a
|
|
|
|
# difference to query-block
|
|
|
|
def test_implicit_node(self):
|
|
|
|
self.assert_no_active_block_jobs()
|
|
|
|
|
|
|
|
result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full',
|
|
|
|
target=self.qmp_target)
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
|
|
|
result = self.vm.qmp('query-block')
|
|
|
|
self.assert_qmp(result, 'return[0]/inserted/file', test_img)
|
|
|
|
self.assert_qmp(result, 'return[0]/inserted/drv', iotests.imgfmt)
|
|
|
|
self.assert_qmp(result, 'return[0]/inserted/backing_file', backing_img)
|
|
|
|
self.assert_qmp(result, 'return[0]/inserted/backing_file_depth', 1)
|
|
|
|
self.assert_qmp(result, 'return[0]/inserted/image/filename', test_img)
|
|
|
|
self.assert_qmp(result, 'return[0]/inserted/image/backing-image/filename', backing_img)
|
|
|
|
|
|
|
|
result = self.vm.qmp('query-blockstats')
|
|
|
|
self.assert_qmp(result, 'return[0]/node-name', 'top')
|
|
|
|
self.assert_qmp(result, 'return[0]/backing/node-name', 'base')
|
|
|
|
|
|
|
|
self.cancel_and_wait(force=True)
|
|
|
|
result = self.vm.qmp('query-block')
|
|
|
|
self.assert_qmp(result, 'return[0]/inserted/file', test_img)
|
|
|
|
self.assert_qmp(result, 'return[0]/inserted/drv', iotests.imgfmt)
|
|
|
|
self.assert_qmp(result, 'return[0]/inserted/backing_file', backing_img)
|
|
|
|
self.assert_qmp(result, 'return[0]/inserted/backing_file_depth', 1)
|
|
|
|
self.assert_qmp(result, 'return[0]/inserted/image/filename', test_img)
|
|
|
|
self.assert_qmp(result, 'return[0]/inserted/image/backing-image/filename', backing_img)
|
|
|
|
|
|
|
|
result = self.vm.qmp('query-blockstats')
|
|
|
|
self.assert_qmp(result, 'return[0]/node-name', 'top')
|
|
|
|
self.assert_qmp(result, 'return[0]/backing/node-name', 'base')
|
|
|
|
|
2012-10-23 16:39:22 +02:00
|
|
|
def test_medium_not_found(self):
|
2015-07-03 09:28:48 +02:00
|
|
|
if iotests.qemu_default_machine != 'pc':
|
|
|
|
return
|
|
|
|
|
2015-12-24 05:45:06 +01:00
|
|
|
result = self.vm.qmp(self.qmp_cmd, device='ide1-cd0', sync='full',
|
|
|
|
target=self.qmp_target)
|
2016-06-23 14:20:24 +02:00
|
|
|
self.assert_qmp(result, 'error/class', 'GenericError')
|
2012-10-23 16:39:22 +02:00
|
|
|
|
|
|
|
def test_image_not_found(self):
|
2015-12-24 05:45:06 +01:00
|
|
|
result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full',
|
|
|
|
mode='existing', target=self.qmp_target)
|
2012-10-23 16:39:22 +02:00
|
|
|
self.assert_qmp(result, 'error/class', 'GenericError')
|
|
|
|
|
|
|
|
def test_device_not_found(self):
|
2015-12-24 05:45:06 +01:00
|
|
|
result = self.vm.qmp(self.qmp_cmd, device='nonexistent', sync='full',
|
|
|
|
target=self.qmp_target)
|
2016-06-23 14:20:24 +02:00
|
|
|
self.assert_qmp(result, 'error/class', 'GenericError')
|
2015-12-24 05:45:06 +01:00
|
|
|
|
|
|
|
class TestSingleBlockdev(TestSingleDrive):
|
|
|
|
qmp_cmd = 'blockdev-mirror'
|
|
|
|
qmp_target = 'node1'
|
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
TestSingleDrive.setUp(self)
|
iotests: Specify explicit backing format where sensible
There are many existing qcow2 images that specify a backing file but
no format. This has been the source of CVEs in the past, but has
become more prominent of a problem now that libvirt has switched to
-blockdev. With older -drive, at least the probing was always done by
qemu (so the only risk of a changed format between successive boots of
a guest was if qemu was upgraded and probed differently). But with
newer -blockdev, libvirt must specify a format; if libvirt guesses raw
where the image was formatted, this results in data corruption visible
to the guest; conversely, if libvirt guesses qcow2 where qemu was
using raw, this can result in potential security holes, so modern
libvirt instead refuses to use images without explicit backing format.
The change in libvirt to reject images without explicit backing format
has pointed out that a number of tools have been far too reliant on
probing in the past. It's time to set a better example in our own
iotests of properly setting this parameter.
iotest calls to create, rebase, and convert are all impacted to some
degree. It's a bit annoying that we are inconsistent on command line
- while all of those accept -o backing_file=...,backing_fmt=..., the
shortcuts are different: create and rebase have -b and -F, while
convert has -B but no -F. (amend has no shortcuts, but the previous
patch just deprecated the use of amend to change backing chains).
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20200706203954.341758-9-eblake@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2020-07-06 22:39:52 +02:00
|
|
|
qemu_img('create', '-f', iotests.imgfmt,
|
|
|
|
'-o', 'backing_file=%s' % backing_img, '-F', 'raw', target_img)
|
2016-10-07 17:05:04 +02:00
|
|
|
args = {'driver': iotests.imgfmt,
|
|
|
|
'node-name': self.qmp_target,
|
|
|
|
'file': { 'filename': target_img, 'driver': 'file' } }
|
2015-12-24 05:45:06 +01:00
|
|
|
result = self.vm.qmp("blockdev-add", **args)
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
2018-08-14 11:52:25 +02:00
|
|
|
def test_mirror_to_self(self):
|
|
|
|
result = self.vm.qmp(self.qmp_cmd, job_id='job0',
|
|
|
|
device=self.qmp_target, sync='full',
|
|
|
|
target=self.qmp_target)
|
|
|
|
self.assert_qmp(result, 'error/class', 'GenericError')
|
|
|
|
|
2020-05-11 15:58:25 +02:00
|
|
|
def do_test_resize(self, device, node):
|
|
|
|
def pre_finalize():
|
|
|
|
if device:
|
|
|
|
result = self.vm.qmp('block_resize', device=device, size=65536)
|
|
|
|
self.assert_qmp(result, 'error/class', 'GenericError')
|
|
|
|
|
|
|
|
result = self.vm.qmp('block_resize', node_name=node, size=65536)
|
|
|
|
self.assert_qmp(result, 'error/class', 'GenericError')
|
|
|
|
|
|
|
|
result = self.vm.qmp(self.qmp_cmd, job_id='job0', device='drive0',
|
|
|
|
sync='full', target=self.qmp_target,
|
|
|
|
auto_finalize=False, auto_dismiss=False)
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
|
|
|
result = self.vm.run_job('job0', auto_finalize=False,
|
|
|
|
pre_finalize=pre_finalize)
|
|
|
|
self.assertEqual(result, None)
|
|
|
|
|
|
|
|
def test_source_resize(self):
|
|
|
|
self.do_test_resize('drive0', 'top')
|
|
|
|
|
|
|
|
def test_target_resize(self):
|
|
|
|
self.do_test_resize(None, self.qmp_target)
|
|
|
|
|
|
|
|
def do_test_target_size(self, size):
|
|
|
|
result = self.vm.qmp('block_resize', node_name=self.qmp_target,
|
|
|
|
size=size)
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
|
|
|
result = self.vm.qmp(self.qmp_cmd, job_id='job0',
|
|
|
|
device='drive0', sync='full', auto_dismiss=False,
|
|
|
|
target=self.qmp_target)
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
|
|
|
result = self.vm.run_job('job0')
|
|
|
|
self.assertEqual(result, 'Source and target image have different sizes')
|
|
|
|
|
2020-06-17 12:48:19 +02:00
|
|
|
# qed does not support shrinking
|
|
|
|
@iotests.skip_for_formats(('qed'))
|
2020-05-11 15:58:25 +02:00
|
|
|
def test_small_target(self):
|
|
|
|
self.do_test_target_size(self.image_len // 2)
|
|
|
|
|
|
|
|
def test_large_target(self):
|
|
|
|
self.do_test_target_size(self.image_len * 2)
|
|
|
|
|
2015-12-24 05:45:06 +01:00
|
|
|
test_large_cluster = None
|
|
|
|
test_image_not_found = None
|
|
|
|
test_small_buffer2 = None
|
|
|
|
|
2014-06-24 14:26:38 +02:00
|
|
|
class TestSingleDriveZeroLength(TestSingleDrive):
|
|
|
|
image_len = 0
|
|
|
|
test_small_buffer2 = None
|
|
|
|
test_large_cluster = None
|
|
|
|
|
2015-12-24 05:45:06 +01:00
|
|
|
class TestSingleBlockdevZeroLength(TestSingleBlockdev):
|
|
|
|
image_len = 0
|
2020-05-11 15:58:25 +02:00
|
|
|
test_small_target = None
|
|
|
|
test_large_target = None
|
2015-12-24 05:45:06 +01:00
|
|
|
|
2014-07-01 16:52:21 +02:00
|
|
|
class TestSingleDriveUnalignedLength(TestSingleDrive):
|
|
|
|
image_len = 1025 * 1024
|
|
|
|
test_small_buffer2 = None
|
|
|
|
test_large_cluster = None
|
|
|
|
|
2015-12-24 05:45:06 +01:00
|
|
|
class TestSingleBlockdevUnalignedLength(TestSingleBlockdev):
|
|
|
|
image_len = 1025 * 1024
|
|
|
|
|
2015-06-08 07:56:12 +02:00
|
|
|
class TestMirrorNoBacking(iotests.QMPTestCase):
|
2012-10-23 16:39:22 +02:00
|
|
|
image_len = 2 * 1024 * 1024 # MB
|
|
|
|
|
|
|
|
def setUp(self):
|
2013-05-28 17:11:37 +02:00
|
|
|
iotests.create_image(backing_img, TestMirrorNoBacking.image_len)
|
iotests: Specify explicit backing format where sensible
There are many existing qcow2 images that specify a backing file but
no format. This has been the source of CVEs in the past, but has
become more prominent of a problem now that libvirt has switched to
-blockdev. With older -drive, at least the probing was always done by
qemu (so the only risk of a changed format between successive boots of
a guest was if qemu was upgraded and probed differently). But with
newer -blockdev, libvirt must specify a format; if libvirt guesses raw
where the image was formatted, this results in data corruption visible
to the guest; conversely, if libvirt guesses qcow2 where qemu was
using raw, this can result in potential security holes, so modern
libvirt instead refuses to use images without explicit backing format.
The change in libvirt to reject images without explicit backing format
has pointed out that a number of tools have been far too reliant on
probing in the past. It's time to set a better example in our own
iotests of properly setting this parameter.
iotest calls to create, rebase, and convert are all impacted to some
degree. It's a bit annoying that we are inconsistent on command line
- while all of those accept -o backing_file=...,backing_fmt=..., the
shortcuts are different: create and rebase have -b and -F, while
convert has -B but no -F. (amend has no shortcuts, but the previous
patch just deprecated the use of amend to change backing chains).
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20200706203954.341758-9-eblake@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2020-07-06 22:39:52 +02:00
|
|
|
qemu_img('create', '-f', iotests.imgfmt,
|
|
|
|
'-o', 'backing_file=%s' % backing_img, '-F', 'raw', test_img)
|
2012-10-23 16:39:22 +02:00
|
|
|
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)
|
2015-06-08 07:56:12 +02:00
|
|
|
try:
|
|
|
|
os.remove(target_backing_img)
|
|
|
|
except:
|
|
|
|
pass
|
2012-10-23 16:39:22 +02:00
|
|
|
os.remove(target_img)
|
|
|
|
|
|
|
|
def test_complete(self):
|
2013-05-28 17:11:34 +02:00
|
|
|
self.assert_no_active_block_jobs()
|
2012-10-23 16:39:22 +02:00
|
|
|
|
iotests: Specify explicit backing format where sensible
There are many existing qcow2 images that specify a backing file but
no format. This has been the source of CVEs in the past, but has
become more prominent of a problem now that libvirt has switched to
-blockdev. With older -drive, at least the probing was always done by
qemu (so the only risk of a changed format between successive boots of
a guest was if qemu was upgraded and probed differently). But with
newer -blockdev, libvirt must specify a format; if libvirt guesses raw
where the image was formatted, this results in data corruption visible
to the guest; conversely, if libvirt guesses qcow2 where qemu was
using raw, this can result in potential security holes, so modern
libvirt instead refuses to use images without explicit backing format.
The change in libvirt to reject images without explicit backing format
has pointed out that a number of tools have been far too reliant on
probing in the past. It's time to set a better example in our own
iotests of properly setting this parameter.
iotest calls to create, rebase, and convert are all impacted to some
degree. It's a bit annoying that we are inconsistent on command line
- while all of those accept -o backing_file=...,backing_fmt=..., the
shortcuts are different: create and rebase have -b and -F, while
convert has -B but no -F. (amend has no shortcuts, but the previous
patch just deprecated the use of amend to change backing chains).
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20200706203954.341758-9-eblake@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2020-07-06 22:39:52 +02:00
|
|
|
qemu_img('create', '-f', iotests.imgfmt,
|
|
|
|
'-o', 'backing_file=%s' % backing_img, '-F', 'raw', target_img)
|
2012-10-23 16:39:22 +02:00
|
|
|
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
|
|
|
mode='existing', target=target_img)
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
|
|
|
self.complete_and_wait()
|
|
|
|
result = self.vm.qmp('query-block')
|
|
|
|
self.assert_qmp(result, 'return[0]/inserted/file', target_img)
|
|
|
|
self.vm.shutdown()
|
2015-06-08 07:56:12 +02:00
|
|
|
self.assertTrue(iotests.compare_images(test_img, target_img),
|
2012-10-23 16:39:22 +02:00
|
|
|
'target image does not match source after mirroring')
|
|
|
|
|
|
|
|
def test_cancel(self):
|
2013-05-28 17:11:34 +02:00
|
|
|
self.assert_no_active_block_jobs()
|
2012-10-23 16:39:22 +02:00
|
|
|
|
iotests: Specify explicit backing format where sensible
There are many existing qcow2 images that specify a backing file but
no format. This has been the source of CVEs in the past, but has
become more prominent of a problem now that libvirt has switched to
-blockdev. With older -drive, at least the probing was always done by
qemu (so the only risk of a changed format between successive boots of
a guest was if qemu was upgraded and probed differently). But with
newer -blockdev, libvirt must specify a format; if libvirt guesses raw
where the image was formatted, this results in data corruption visible
to the guest; conversely, if libvirt guesses qcow2 where qemu was
using raw, this can result in potential security holes, so modern
libvirt instead refuses to use images without explicit backing format.
The change in libvirt to reject images without explicit backing format
has pointed out that a number of tools have been far too reliant on
probing in the past. It's time to set a better example in our own
iotests of properly setting this parameter.
iotest calls to create, rebase, and convert are all impacted to some
degree. It's a bit annoying that we are inconsistent on command line
- while all of those accept -o backing_file=...,backing_fmt=..., the
shortcuts are different: create and rebase have -b and -F, while
convert has -B but no -F. (amend has no shortcuts, but the previous
patch just deprecated the use of amend to change backing chains).
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20200706203954.341758-9-eblake@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2020-07-06 22:39:52 +02:00
|
|
|
qemu_img('create', '-f', iotests.imgfmt,
|
|
|
|
'-o', 'backing_file=%s' % backing_img, '-F', 'raw', target_img)
|
2012-10-23 16:39:22 +02:00
|
|
|
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
|
|
|
mode='existing', target=target_img)
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
2013-05-28 17:11:35 +02:00
|
|
|
self.wait_ready_and_cancel()
|
2012-10-23 16:39:22 +02:00
|
|
|
result = self.vm.qmp('query-block')
|
|
|
|
self.assert_qmp(result, 'return[0]/inserted/file', test_img)
|
|
|
|
self.vm.shutdown()
|
2015-06-08 07:56:12 +02:00
|
|
|
self.assertTrue(iotests.compare_images(test_img, target_img),
|
2012-10-23 16:39:22 +02:00
|
|
|
'target image does not match source after mirroring')
|
|
|
|
|
2013-01-21 17:09:43 +01:00
|
|
|
def test_large_cluster(self):
|
2013-05-28 17:11:34 +02:00
|
|
|
self.assert_no_active_block_jobs()
|
2013-01-21 17:09:43 +01:00
|
|
|
|
|
|
|
# qemu-img create fails if the image is not there
|
|
|
|
qemu_img('create', '-f', iotests.imgfmt, '-o', 'size=%d'
|
|
|
|
%(TestMirrorNoBacking.image_len), target_backing_img)
|
|
|
|
qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,backing_file=%s'
|
iotests: Specify explicit backing format where sensible
There are many existing qcow2 images that specify a backing file but
no format. This has been the source of CVEs in the past, but has
become more prominent of a problem now that libvirt has switched to
-blockdev. With older -drive, at least the probing was always done by
qemu (so the only risk of a changed format between successive boots of
a guest was if qemu was upgraded and probed differently). But with
newer -blockdev, libvirt must specify a format; if libvirt guesses raw
where the image was formatted, this results in data corruption visible
to the guest; conversely, if libvirt guesses qcow2 where qemu was
using raw, this can result in potential security holes, so modern
libvirt instead refuses to use images without explicit backing format.
The change in libvirt to reject images without explicit backing format
has pointed out that a number of tools have been far too reliant on
probing in the past. It's time to set a better example in our own
iotests of properly setting this parameter.
iotest calls to create, rebase, and convert are all impacted to some
degree. It's a bit annoying that we are inconsistent on command line
- while all of those accept -o backing_file=...,backing_fmt=..., the
shortcuts are different: create and rebase have -b and -F, while
convert has -B but no -F. (amend has no shortcuts, but the previous
patch just deprecated the use of amend to change backing chains).
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20200706203954.341758-9-eblake@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2020-07-06 22:39:52 +02:00
|
|
|
% (TestMirrorNoBacking.image_len, target_backing_img),
|
|
|
|
'-F', iotests.imgfmt, target_img)
|
2013-01-21 17:09:43 +01:00
|
|
|
|
|
|
|
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
|
|
|
mode='existing', target=target_img)
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
|
|
|
self.complete_and_wait()
|
|
|
|
result = self.vm.qmp('query-block')
|
|
|
|
self.assert_qmp(result, 'return[0]/inserted/file', target_img)
|
|
|
|
self.vm.shutdown()
|
2015-06-08 07:56:12 +02:00
|
|
|
self.assertTrue(iotests.compare_images(test_img, target_img),
|
2013-01-21 17:09:43 +01:00
|
|
|
'target image does not match source after mirroring')
|
|
|
|
|
2015-06-08 07:56:12 +02:00
|
|
|
class TestMirrorResized(iotests.QMPTestCase):
|
2013-01-25 19:57:20 +01:00
|
|
|
backing_len = 1 * 1024 * 1024 # MB
|
|
|
|
image_len = 2 * 1024 * 1024 # MB
|
|
|
|
|
|
|
|
def setUp(self):
|
2013-05-28 17:11:37 +02:00
|
|
|
iotests.create_image(backing_img, TestMirrorResized.backing_len)
|
iotests: Specify explicit backing format where sensible
There are many existing qcow2 images that specify a backing file but
no format. This has been the source of CVEs in the past, but has
become more prominent of a problem now that libvirt has switched to
-blockdev. With older -drive, at least the probing was always done by
qemu (so the only risk of a changed format between successive boots of
a guest was if qemu was upgraded and probed differently). But with
newer -blockdev, libvirt must specify a format; if libvirt guesses raw
where the image was formatted, this results in data corruption visible
to the guest; conversely, if libvirt guesses qcow2 where qemu was
using raw, this can result in potential security holes, so modern
libvirt instead refuses to use images without explicit backing format.
The change in libvirt to reject images without explicit backing format
has pointed out that a number of tools have been far too reliant on
probing in the past. It's time to set a better example in our own
iotests of properly setting this parameter.
iotest calls to create, rebase, and convert are all impacted to some
degree. It's a bit annoying that we are inconsistent on command line
- while all of those accept -o backing_file=...,backing_fmt=..., the
shortcuts are different: create and rebase have -b and -F, while
convert has -B but no -F. (amend has no shortcuts, but the previous
patch just deprecated the use of amend to change backing chains).
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20200706203954.341758-9-eblake@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2020-07-06 22:39:52 +02:00
|
|
|
qemu_img('create', '-f', iotests.imgfmt,
|
|
|
|
'-o', 'backing_file=%s' % backing_img, '-F', 'raw', test_img)
|
2013-01-25 19:57:20 +01:00
|
|
|
qemu_img('resize', test_img, '2M')
|
|
|
|
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):
|
2013-05-28 17:11:34 +02:00
|
|
|
self.assert_no_active_block_jobs()
|
2013-01-25 19:57:20 +01:00
|
|
|
|
|
|
|
result = self.vm.qmp('drive-mirror', device='drive0', sync='top',
|
|
|
|
target=target_img)
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
|
|
|
self.complete_and_wait()
|
|
|
|
result = self.vm.qmp('query-block')
|
|
|
|
self.assert_qmp(result, 'return[0]/inserted/file', target_img)
|
|
|
|
self.vm.shutdown()
|
2013-05-28 17:11:36 +02:00
|
|
|
self.assertTrue(iotests.compare_images(test_img, target_img),
|
2013-01-25 19:57:20 +01:00
|
|
|
'target image does not match source after mirroring')
|
|
|
|
|
|
|
|
def test_complete_full(self):
|
2013-05-28 17:11:34 +02:00
|
|
|
self.assert_no_active_block_jobs()
|
2013-01-25 19:57:20 +01:00
|
|
|
|
|
|
|
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
|
|
|
target=target_img)
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
|
|
|
self.complete_and_wait()
|
|
|
|
result = self.vm.qmp('query-block')
|
|
|
|
self.assert_qmp(result, 'return[0]/inserted/file', target_img)
|
|
|
|
self.vm.shutdown()
|
2013-05-28 17:11:36 +02:00
|
|
|
self.assertTrue(iotests.compare_images(test_img, target_img),
|
2013-01-25 19:57:20 +01:00
|
|
|
'target image does not match source after mirroring')
|
|
|
|
|
2015-06-08 07:56:12 +02:00
|
|
|
class TestReadErrors(iotests.QMPTestCase):
|
2012-10-23 16:39:23 +02:00
|
|
|
image_len = 2 * 1024 * 1024 # MB
|
|
|
|
|
|
|
|
# this should be a multiple of twice the default granularity
|
|
|
|
# so that we hit this offset first in state 1
|
|
|
|
MIRROR_GRANULARITY = 1024 * 1024
|
|
|
|
|
|
|
|
def create_blkdebug_file(self, name, event, errno):
|
|
|
|
file = open(name, 'w')
|
|
|
|
file.write('''
|
|
|
|
[inject-error]
|
|
|
|
state = "1"
|
|
|
|
event = "%s"
|
|
|
|
errno = "%d"
|
|
|
|
immediately = "off"
|
|
|
|
once = "on"
|
|
|
|
sector = "%d"
|
|
|
|
|
|
|
|
[set-state]
|
|
|
|
state = "1"
|
|
|
|
event = "%s"
|
|
|
|
new_state = "2"
|
|
|
|
|
|
|
|
[set-state]
|
|
|
|
state = "2"
|
|
|
|
event = "%s"
|
|
|
|
new_state = "1"
|
2018-10-22 15:53:02 +02:00
|
|
|
''' % (event, errno, self.MIRROR_GRANULARITY // 512, event, event))
|
2012-10-23 16:39:23 +02:00
|
|
|
file.close()
|
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
self.blkdebug_file = backing_img + ".blkdebug"
|
2013-05-28 17:11:37 +02:00
|
|
|
iotests.create_image(backing_img, TestReadErrors.image_len)
|
2012-10-23 16:39:23 +02:00
|
|
|
self.create_blkdebug_file(self.blkdebug_file, "read_aio", 5)
|
|
|
|
qemu_img('create', '-f', iotests.imgfmt,
|
|
|
|
'-o', 'backing_file=blkdebug:%s:%s,backing_fmt=raw'
|
|
|
|
% (self.blkdebug_file, backing_img),
|
|
|
|
test_img)
|
2013-01-21 17:09:43 +01:00
|
|
|
# Write something for tests that use sync='top'
|
|
|
|
qemu_io('-c', 'write %d 512' % (self.MIRROR_GRANULARITY + 65536),
|
|
|
|
test_img)
|
2012-10-23 16:39:23 +02:00
|
|
|
self.vm = iotests.VM().add_drive(test_img)
|
|
|
|
self.vm.launch()
|
|
|
|
|
|
|
|
def tearDown(self):
|
|
|
|
self.vm.shutdown()
|
|
|
|
os.remove(test_img)
|
2017-07-27 15:13:20 +02:00
|
|
|
os.remove(target_img)
|
2012-10-23 16:39:23 +02:00
|
|
|
os.remove(backing_img)
|
|
|
|
os.remove(self.blkdebug_file)
|
|
|
|
|
|
|
|
def test_report_read(self):
|
2013-05-28 17:11:34 +02:00
|
|
|
self.assert_no_active_block_jobs()
|
2012-10-23 16:39:23 +02:00
|
|
|
|
|
|
|
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
|
|
|
target=target_img)
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
|
|
|
completed = False
|
|
|
|
error = False
|
|
|
|
while not completed:
|
|
|
|
for event in self.vm.get_qmp_events(wait=True):
|
|
|
|
if event['event'] == 'BLOCK_JOB_ERROR':
|
|
|
|
self.assert_qmp(event, 'data/device', 'drive0')
|
|
|
|
self.assert_qmp(event, 'data/operation', 'read')
|
|
|
|
error = True
|
|
|
|
elif event['event'] == 'BLOCK_JOB_READY':
|
|
|
|
self.assertTrue(False, 'job completed unexpectedly')
|
|
|
|
elif event['event'] == 'BLOCK_JOB_COMPLETED':
|
|
|
|
self.assertTrue(error, 'job completed unexpectedly')
|
|
|
|
self.assert_qmp(event, 'data/type', 'mirror')
|
|
|
|
self.assert_qmp(event, 'data/device', 'drive0')
|
|
|
|
self.assert_qmp(event, 'data/error', 'Input/output error')
|
|
|
|
completed = True
|
2018-04-30 19:09:46 +02:00
|
|
|
elif event['event'] == 'JOB_STATUS_CHANGE':
|
|
|
|
self.assert_qmp(event, 'data/id', 'drive0')
|
2012-10-23 16:39:23 +02:00
|
|
|
|
2013-05-28 17:11:34 +02:00
|
|
|
self.assert_no_active_block_jobs()
|
2012-10-23 16:39:23 +02:00
|
|
|
|
|
|
|
def test_ignore_read(self):
|
2013-05-28 17:11:34 +02:00
|
|
|
self.assert_no_active_block_jobs()
|
2012-10-23 16:39:23 +02:00
|
|
|
|
|
|
|
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
|
|
|
target=target_img, on_source_error='ignore')
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
|
|
|
event = self.vm.get_qmp_event(wait=True)
|
2018-04-30 19:09:46 +02:00
|
|
|
while event['event'] == 'JOB_STATUS_CHANGE':
|
|
|
|
self.assert_qmp(event, 'data/id', 'drive0')
|
|
|
|
event = self.vm.get_qmp_event(wait=True)
|
|
|
|
|
2018-11-20 18:12:21 +01:00
|
|
|
self.assertEqual(event['event'], 'BLOCK_JOB_ERROR')
|
2012-10-23 16:39:23 +02:00
|
|
|
self.assert_qmp(event, 'data/device', 'drive0')
|
|
|
|
self.assert_qmp(event, 'data/operation', 'read')
|
|
|
|
result = self.vm.qmp('query-block-jobs')
|
|
|
|
self.assert_qmp(result, 'return[0]/paused', False)
|
|
|
|
self.complete_and_wait()
|
|
|
|
|
2013-01-21 17:09:43 +01:00
|
|
|
def test_large_cluster(self):
|
2013-05-28 17:11:34 +02:00
|
|
|
self.assert_no_active_block_jobs()
|
2013-01-21 17:09:43 +01:00
|
|
|
|
|
|
|
# Test COW into the target image. The first half of the
|
|
|
|
# cluster at MIRROR_GRANULARITY has to be copied from
|
|
|
|
# backing_img, even though sync='top'.
|
iotests: Specify explicit backing format where sensible
There are many existing qcow2 images that specify a backing file but
no format. This has been the source of CVEs in the past, but has
become more prominent of a problem now that libvirt has switched to
-blockdev. With older -drive, at least the probing was always done by
qemu (so the only risk of a changed format between successive boots of
a guest was if qemu was upgraded and probed differently). But with
newer -blockdev, libvirt must specify a format; if libvirt guesses raw
where the image was formatted, this results in data corruption visible
to the guest; conversely, if libvirt guesses qcow2 where qemu was
using raw, this can result in potential security holes, so modern
libvirt instead refuses to use images without explicit backing format.
The change in libvirt to reject images without explicit backing format
has pointed out that a number of tools have been far too reliant on
probing in the past. It's time to set a better example in our own
iotests of properly setting this parameter.
iotest calls to create, rebase, and convert are all impacted to some
degree. It's a bit annoying that we are inconsistent on command line
- while all of those accept -o backing_file=...,backing_fmt=..., the
shortcuts are different: create and rebase have -b and -F, while
convert has -B but no -F. (amend has no shortcuts, but the previous
patch just deprecated the use of amend to change backing chains).
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20200706203954.341758-9-eblake@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2020-07-06 22:39:52 +02:00
|
|
|
qemu_img('create', '-f', iotests.imgfmt,
|
|
|
|
'-ocluster_size=131072,backing_file=%s' %(backing_img),
|
|
|
|
'-F', 'raw', target_img)
|
2013-01-21 17:09:43 +01:00
|
|
|
result = self.vm.qmp('drive-mirror', device='drive0', sync='top',
|
|
|
|
on_source_error='ignore',
|
|
|
|
mode='existing', target=target_img)
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
|
|
|
event = self.vm.get_qmp_event(wait=True)
|
2018-04-30 19:09:46 +02:00
|
|
|
while event['event'] == 'JOB_STATUS_CHANGE':
|
|
|
|
self.assert_qmp(event, 'data/id', 'drive0')
|
|
|
|
event = self.vm.get_qmp_event(wait=True)
|
|
|
|
|
2018-11-20 18:12:21 +01:00
|
|
|
self.assertEqual(event['event'], 'BLOCK_JOB_ERROR')
|
2013-01-21 17:09:43 +01:00
|
|
|
self.assert_qmp(event, 'data/device', 'drive0')
|
|
|
|
self.assert_qmp(event, 'data/operation', 'read')
|
|
|
|
result = self.vm.qmp('query-block-jobs')
|
|
|
|
self.assert_qmp(result, 'return[0]/paused', False)
|
|
|
|
self.complete_and_wait()
|
|
|
|
self.vm.shutdown()
|
|
|
|
|
|
|
|
# Detach blkdebug to compare images successfully
|
iotests: Specify explicit backing format where sensible
There are many existing qcow2 images that specify a backing file but
no format. This has been the source of CVEs in the past, but has
become more prominent of a problem now that libvirt has switched to
-blockdev. With older -drive, at least the probing was always done by
qemu (so the only risk of a changed format between successive boots of
a guest was if qemu was upgraded and probed differently). But with
newer -blockdev, libvirt must specify a format; if libvirt guesses raw
where the image was formatted, this results in data corruption visible
to the guest; conversely, if libvirt guesses qcow2 where qemu was
using raw, this can result in potential security holes, so modern
libvirt instead refuses to use images without explicit backing format.
The change in libvirt to reject images without explicit backing format
has pointed out that a number of tools have been far too reliant on
probing in the past. It's time to set a better example in our own
iotests of properly setting this parameter.
iotest calls to create, rebase, and convert are all impacted to some
degree. It's a bit annoying that we are inconsistent on command line
- while all of those accept -o backing_file=...,backing_fmt=..., the
shortcuts are different: create and rebase have -b and -F, while
convert has -B but no -F. (amend has no shortcuts, but the previous
patch just deprecated the use of amend to change backing chains).
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20200706203954.341758-9-eblake@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2020-07-06 22:39:52 +02:00
|
|
|
qemu_img('rebase', '-f', iotests.imgfmt, '-u', '-b', backing_img,
|
|
|
|
'-F', 'raw', test_img)
|
2013-05-28 17:11:36 +02:00
|
|
|
self.assertTrue(iotests.compare_images(test_img, target_img),
|
2013-01-21 17:09:43 +01:00
|
|
|
'target image does not match source after mirroring')
|
|
|
|
|
2012-10-23 16:39:23 +02:00
|
|
|
def test_stop_read(self):
|
2013-05-28 17:11:34 +02:00
|
|
|
self.assert_no_active_block_jobs()
|
2012-10-23 16:39:23 +02:00
|
|
|
|
|
|
|
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
|
|
|
target=target_img, on_source_error='stop')
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
|
|
|
error = False
|
|
|
|
ready = False
|
|
|
|
while not ready:
|
|
|
|
for event in self.vm.get_qmp_events(wait=True):
|
|
|
|
if event['event'] == 'BLOCK_JOB_ERROR':
|
|
|
|
self.assert_qmp(event, 'data/device', 'drive0')
|
|
|
|
self.assert_qmp(event, 'data/operation', 'read')
|
|
|
|
|
|
|
|
result = self.vm.qmp('query-block-jobs')
|
|
|
|
self.assert_qmp(result, 'return[0]/paused', True)
|
|
|
|
self.assert_qmp(result, 'return[0]/io-status', 'failed')
|
|
|
|
|
|
|
|
result = self.vm.qmp('block-job-resume', device='drive0')
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
error = True
|
|
|
|
elif event['event'] == 'BLOCK_JOB_READY':
|
|
|
|
self.assertTrue(error, 'job completed unexpectedly')
|
|
|
|
self.assert_qmp(event, 'data/device', 'drive0')
|
|
|
|
ready = True
|
|
|
|
|
|
|
|
result = self.vm.qmp('query-block-jobs')
|
|
|
|
self.assert_qmp(result, 'return[0]/paused', False)
|
|
|
|
self.assert_qmp(result, 'return[0]/io-status', 'ok')
|
|
|
|
|
|
|
|
self.complete_and_wait(wait_ready=False)
|
2013-05-28 17:11:34 +02:00
|
|
|
self.assert_no_active_block_jobs()
|
2012-10-23 16:39:23 +02:00
|
|
|
|
2015-06-08 07:56:12 +02:00
|
|
|
class TestWriteErrors(iotests.QMPTestCase):
|
2012-10-23 16:39:23 +02:00
|
|
|
image_len = 2 * 1024 * 1024 # MB
|
|
|
|
|
|
|
|
# this should be a multiple of twice the default granularity
|
|
|
|
# so that we hit this offset first in state 1
|
|
|
|
MIRROR_GRANULARITY = 1024 * 1024
|
|
|
|
|
|
|
|
def create_blkdebug_file(self, name, event, errno):
|
|
|
|
file = open(name, 'w')
|
|
|
|
file.write('''
|
|
|
|
[inject-error]
|
|
|
|
state = "1"
|
|
|
|
event = "%s"
|
|
|
|
errno = "%d"
|
|
|
|
immediately = "off"
|
|
|
|
once = "on"
|
|
|
|
sector = "%d"
|
|
|
|
|
|
|
|
[set-state]
|
|
|
|
state = "1"
|
|
|
|
event = "%s"
|
|
|
|
new_state = "2"
|
|
|
|
|
|
|
|
[set-state]
|
|
|
|
state = "2"
|
|
|
|
event = "%s"
|
|
|
|
new_state = "1"
|
2018-10-22 15:53:02 +02:00
|
|
|
''' % (event, errno, self.MIRROR_GRANULARITY // 512, event, event))
|
2012-10-23 16:39:23 +02:00
|
|
|
file.close()
|
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
self.blkdebug_file = target_img + ".blkdebug"
|
2013-05-28 17:11:37 +02:00
|
|
|
iotests.create_image(backing_img, TestWriteErrors.image_len)
|
2012-10-23 16:39:23 +02:00
|
|
|
self.create_blkdebug_file(self.blkdebug_file, "write_aio", 5)
|
iotests: Specify explicit backing format where sensible
There are many existing qcow2 images that specify a backing file but
no format. This has been the source of CVEs in the past, but has
become more prominent of a problem now that libvirt has switched to
-blockdev. With older -drive, at least the probing was always done by
qemu (so the only risk of a changed format between successive boots of
a guest was if qemu was upgraded and probed differently). But with
newer -blockdev, libvirt must specify a format; if libvirt guesses raw
where the image was formatted, this results in data corruption visible
to the guest; conversely, if libvirt guesses qcow2 where qemu was
using raw, this can result in potential security holes, so modern
libvirt instead refuses to use images without explicit backing format.
The change in libvirt to reject images without explicit backing format
has pointed out that a number of tools have been far too reliant on
probing in the past. It's time to set a better example in our own
iotests of properly setting this parameter.
iotest calls to create, rebase, and convert are all impacted to some
degree. It's a bit annoying that we are inconsistent on command line
- while all of those accept -o backing_file=...,backing_fmt=..., the
shortcuts are different: create and rebase have -b and -F, while
convert has -B but no -F. (amend has no shortcuts, but the previous
patch just deprecated the use of amend to change backing chains).
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20200706203954.341758-9-eblake@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2020-07-06 22:39:52 +02:00
|
|
|
qemu_img('create', '-f', iotests.imgfmt,
|
|
|
|
'-obacking_file=%s' %(backing_img), '-F', 'raw', test_img)
|
2012-10-23 16:39:23 +02:00
|
|
|
self.vm = iotests.VM().add_drive(test_img)
|
|
|
|
self.target_img = 'blkdebug:%s:%s' % (self.blkdebug_file, target_img)
|
|
|
|
qemu_img('create', '-f', iotests.imgfmt, '-osize=%d' %(TestWriteErrors.image_len), target_img)
|
|
|
|
self.vm.launch()
|
|
|
|
|
|
|
|
def tearDown(self):
|
|
|
|
self.vm.shutdown()
|
|
|
|
os.remove(test_img)
|
2017-07-27 15:13:20 +02:00
|
|
|
os.remove(target_img)
|
2012-10-23 16:39:23 +02:00
|
|
|
os.remove(backing_img)
|
|
|
|
os.remove(self.blkdebug_file)
|
|
|
|
|
|
|
|
def test_report_write(self):
|
2013-05-28 17:11:34 +02:00
|
|
|
self.assert_no_active_block_jobs()
|
2012-10-23 16:39:23 +02:00
|
|
|
|
|
|
|
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
|
|
|
mode='existing', target=self.target_img)
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
|
|
|
completed = False
|
|
|
|
error = False
|
|
|
|
while not completed:
|
|
|
|
for event in self.vm.get_qmp_events(wait=True):
|
|
|
|
if event['event'] == 'BLOCK_JOB_ERROR':
|
|
|
|
self.assert_qmp(event, 'data/device', 'drive0')
|
|
|
|
self.assert_qmp(event, 'data/operation', 'write')
|
|
|
|
error = True
|
|
|
|
elif event['event'] == 'BLOCK_JOB_READY':
|
|
|
|
self.assertTrue(False, 'job completed unexpectedly')
|
|
|
|
elif event['event'] == 'BLOCK_JOB_COMPLETED':
|
|
|
|
self.assertTrue(error, 'job completed unexpectedly')
|
|
|
|
self.assert_qmp(event, 'data/type', 'mirror')
|
|
|
|
self.assert_qmp(event, 'data/device', 'drive0')
|
|
|
|
self.assert_qmp(event, 'data/error', 'Input/output error')
|
|
|
|
completed = True
|
|
|
|
|
2013-05-28 17:11:34 +02:00
|
|
|
self.assert_no_active_block_jobs()
|
2012-10-23 16:39:23 +02:00
|
|
|
|
|
|
|
def test_ignore_write(self):
|
2013-05-28 17:11:34 +02:00
|
|
|
self.assert_no_active_block_jobs()
|
2012-10-23 16:39:23 +02:00
|
|
|
|
|
|
|
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
|
|
|
mode='existing', target=self.target_img,
|
|
|
|
on_target_error='ignore')
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
2018-04-30 19:09:46 +02:00
|
|
|
event = self.vm.event_wait(name='BLOCK_JOB_ERROR')
|
2018-11-20 18:12:21 +01:00
|
|
|
self.assertEqual(event['event'], 'BLOCK_JOB_ERROR')
|
2012-10-23 16:39:23 +02:00
|
|
|
self.assert_qmp(event, 'data/device', 'drive0')
|
|
|
|
self.assert_qmp(event, 'data/operation', 'write')
|
|
|
|
result = self.vm.qmp('query-block-jobs')
|
|
|
|
self.assert_qmp(result, 'return[0]/paused', False)
|
|
|
|
self.complete_and_wait()
|
|
|
|
|
|
|
|
def test_stop_write(self):
|
2013-05-28 17:11:34 +02:00
|
|
|
self.assert_no_active_block_jobs()
|
2012-10-23 16:39:23 +02:00
|
|
|
|
|
|
|
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
|
|
|
mode='existing', target=self.target_img,
|
|
|
|
on_target_error='stop')
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
|
|
|
error = False
|
|
|
|
ready = False
|
|
|
|
while not ready:
|
|
|
|
for event in self.vm.get_qmp_events(wait=True):
|
|
|
|
if event['event'] == 'BLOCK_JOB_ERROR':
|
|
|
|
self.assert_qmp(event, 'data/device', 'drive0')
|
|
|
|
self.assert_qmp(event, 'data/operation', 'write')
|
|
|
|
|
|
|
|
result = self.vm.qmp('query-block-jobs')
|
|
|
|
self.assert_qmp(result, 'return[0]/paused', True)
|
|
|
|
self.assert_qmp(result, 'return[0]/io-status', 'failed')
|
|
|
|
|
|
|
|
result = self.vm.qmp('block-job-resume', device='drive0')
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
|
|
|
result = self.vm.qmp('query-block-jobs')
|
|
|
|
self.assert_qmp(result, 'return[0]/paused', False)
|
|
|
|
self.assert_qmp(result, 'return[0]/io-status', 'ok')
|
|
|
|
error = True
|
|
|
|
elif event['event'] == 'BLOCK_JOB_READY':
|
|
|
|
self.assertTrue(error, 'job completed unexpectedly')
|
|
|
|
self.assert_qmp(event, 'data/device', 'drive0')
|
|
|
|
ready = True
|
|
|
|
|
|
|
|
self.complete_and_wait(wait_ready=False)
|
2013-05-28 17:11:34 +02:00
|
|
|
self.assert_no_active_block_jobs()
|
2012-10-23 16:39:23 +02:00
|
|
|
|
2015-06-08 07:56:12 +02:00
|
|
|
class TestSetSpeed(iotests.QMPTestCase):
|
2012-10-23 16:39:22 +02:00
|
|
|
image_len = 80 * 1024 * 1024 # MB
|
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
qemu_img('create', backing_img, str(TestSetSpeed.image_len))
|
iotests: Specify explicit backing format where sensible
There are many existing qcow2 images that specify a backing file but
no format. This has been the source of CVEs in the past, but has
become more prominent of a problem now that libvirt has switched to
-blockdev. With older -drive, at least the probing was always done by
qemu (so the only risk of a changed format between successive boots of
a guest was if qemu was upgraded and probed differently). But with
newer -blockdev, libvirt must specify a format; if libvirt guesses raw
where the image was formatted, this results in data corruption visible
to the guest; conversely, if libvirt guesses qcow2 where qemu was
using raw, this can result in potential security holes, so modern
libvirt instead refuses to use images without explicit backing format.
The change in libvirt to reject images without explicit backing format
has pointed out that a number of tools have been far too reliant on
probing in the past. It's time to set a better example in our own
iotests of properly setting this parameter.
iotest calls to create, rebase, and convert are all impacted to some
degree. It's a bit annoying that we are inconsistent on command line
- while all of those accept -o backing_file=...,backing_fmt=..., the
shortcuts are different: create and rebase have -b and -F, while
convert has -B but no -F. (amend has no shortcuts, but the previous
patch just deprecated the use of amend to change backing chains).
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20200706203954.341758-9-eblake@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2020-07-06 22:39:52 +02:00
|
|
|
qemu_img('create', '-f', iotests.imgfmt,
|
|
|
|
'-o', 'backing_file=%s' % backing_img, '-F', 'raw', test_img)
|
2012-10-23 16:39:22 +02:00
|
|
|
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)
|
|
|
|
os.remove(target_img)
|
|
|
|
|
|
|
|
def test_set_speed(self):
|
2013-05-28 17:11:34 +02:00
|
|
|
self.assert_no_active_block_jobs()
|
2012-10-23 16:39:22 +02:00
|
|
|
|
|
|
|
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
|
|
|
target=target_img)
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
|
|
|
# Default speed is 0
|
|
|
|
result = self.vm.qmp('query-block-jobs')
|
|
|
|
self.assert_qmp(result, 'return[0]/device', 'drive0')
|
|
|
|
self.assert_qmp(result, 'return[0]/speed', 0)
|
|
|
|
|
|
|
|
result = self.vm.qmp('block-job-set-speed', device='drive0', speed=8 * 1024 * 1024)
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
|
|
|
# Ensure the speed we set was accepted
|
|
|
|
result = self.vm.qmp('query-block-jobs')
|
|
|
|
self.assert_qmp(result, 'return[0]/device', 'drive0')
|
|
|
|
self.assert_qmp(result, 'return[0]/speed', 8 * 1024 * 1024)
|
|
|
|
|
2013-05-28 17:11:35 +02:00
|
|
|
self.wait_ready_and_cancel()
|
2012-10-23 16:39:22 +02:00
|
|
|
|
|
|
|
# Check setting speed in drive-mirror works
|
|
|
|
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
|
|
|
target=target_img, speed=4*1024*1024)
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
|
|
|
result = self.vm.qmp('query-block-jobs')
|
|
|
|
self.assert_qmp(result, 'return[0]/device', 'drive0')
|
|
|
|
self.assert_qmp(result, 'return[0]/speed', 4 * 1024 * 1024)
|
|
|
|
|
2013-05-28 17:11:35 +02:00
|
|
|
self.wait_ready_and_cancel()
|
2012-10-23 16:39:22 +02:00
|
|
|
|
|
|
|
def test_set_speed_invalid(self):
|
2013-05-28 17:11:34 +02:00
|
|
|
self.assert_no_active_block_jobs()
|
2012-10-23 16:39:22 +02:00
|
|
|
|
|
|
|
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
|
|
|
target=target_img, speed=-1)
|
|
|
|
self.assert_qmp(result, 'error/class', 'GenericError')
|
|
|
|
|
2013-05-28 17:11:34 +02:00
|
|
|
self.assert_no_active_block_jobs()
|
2012-10-23 16:39:22 +02:00
|
|
|
|
|
|
|
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
|
|
|
target=target_img)
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
|
|
|
result = self.vm.qmp('block-job-set-speed', device='drive0', speed=-1)
|
|
|
|
self.assert_qmp(result, 'error/class', 'GenericError')
|
|
|
|
|
2013-05-28 17:11:35 +02:00
|
|
|
self.wait_ready_and_cancel()
|
2012-10-23 16:39:22 +02:00
|
|
|
|
2015-06-08 07:56:12 +02:00
|
|
|
class TestUnbackedSource(iotests.QMPTestCase):
|
2013-11-06 19:50:45 +01:00
|
|
|
image_len = 2 * 1024 * 1024 # MB
|
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
qemu_img('create', '-f', iotests.imgfmt, test_img,
|
|
|
|
str(TestUnbackedSource.image_len))
|
2019-07-24 19:12:39 +02:00
|
|
|
self.vm = iotests.VM()
|
2013-11-06 19:50:45 +01:00
|
|
|
self.vm.launch()
|
2019-07-24 19:12:39 +02:00
|
|
|
result = self.vm.qmp('blockdev-add', node_name='drive0',
|
|
|
|
driver=iotests.imgfmt,
|
|
|
|
file={
|
|
|
|
'driver': 'file',
|
|
|
|
'filename': test_img,
|
|
|
|
})
|
|
|
|
self.assert_qmp(result, 'return', {})
|
2013-11-06 19:50:45 +01:00
|
|
|
|
|
|
|
def tearDown(self):
|
|
|
|
self.vm.shutdown()
|
|
|
|
os.remove(test_img)
|
|
|
|
os.remove(target_img)
|
|
|
|
|
2013-11-25 20:28:56 +01:00
|
|
|
def test_absolute_paths_full(self):
|
|
|
|
self.assert_no_active_block_jobs()
|
2019-07-24 19:12:39 +02:00
|
|
|
result = self.vm.qmp('drive-mirror', job_id='drive0', device='drive0',
|
2013-11-25 20:28:56 +01:00
|
|
|
sync='full', target=target_img,
|
|
|
|
mode='absolute-paths')
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
self.complete_and_wait()
|
|
|
|
self.assert_no_active_block_jobs()
|
|
|
|
|
|
|
|
def test_absolute_paths_top(self):
|
|
|
|
self.assert_no_active_block_jobs()
|
2019-07-24 19:12:39 +02:00
|
|
|
result = self.vm.qmp('drive-mirror', job_id='drive0', device='drive0',
|
2013-11-25 20:28:56 +01:00
|
|
|
sync='top', target=target_img,
|
|
|
|
mode='absolute-paths')
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
self.complete_and_wait()
|
2013-11-06 19:50:45 +01:00
|
|
|
self.assert_no_active_block_jobs()
|
|
|
|
|
2013-11-25 20:28:56 +01:00
|
|
|
def test_absolute_paths_none(self):
|
|
|
|
self.assert_no_active_block_jobs()
|
2019-07-24 19:12:39 +02:00
|
|
|
result = self.vm.qmp('drive-mirror', job_id='drive0', device='drive0',
|
2013-11-25 20:28:56 +01:00
|
|
|
sync='none', target=target_img,
|
|
|
|
mode='absolute-paths')
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
self.complete_and_wait()
|
|
|
|
self.assert_no_active_block_jobs()
|
2013-11-06 19:50:45 +01:00
|
|
|
|
2019-07-24 19:12:39 +02:00
|
|
|
def test_existing_full(self):
|
|
|
|
qemu_img('create', '-f', iotests.imgfmt, target_img,
|
|
|
|
str(self.image_len))
|
|
|
|
qemu_io('-c', 'write -P 42 0 64k', target_img)
|
|
|
|
|
|
|
|
self.assert_no_active_block_jobs()
|
|
|
|
result = self.vm.qmp('drive-mirror', job_id='drive0', device='drive0',
|
|
|
|
sync='full', target=target_img, mode='existing')
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
self.complete_and_wait()
|
|
|
|
self.assert_no_active_block_jobs()
|
|
|
|
|
|
|
|
result = self.vm.qmp('blockdev-del', node_name='drive0')
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
|
|
|
self.assertTrue(iotests.compare_images(test_img, target_img),
|
|
|
|
'target image does not match source after mirroring')
|
|
|
|
|
|
|
|
def test_blockdev_full(self):
|
|
|
|
qemu_img('create', '-f', iotests.imgfmt, target_img,
|
|
|
|
str(self.image_len))
|
|
|
|
qemu_io('-c', 'write -P 42 0 64k', target_img)
|
|
|
|
|
|
|
|
result = self.vm.qmp('blockdev-add', node_name='target',
|
|
|
|
driver=iotests.imgfmt,
|
|
|
|
file={
|
|
|
|
'driver': 'file',
|
|
|
|
'filename': target_img,
|
|
|
|
})
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
|
|
|
self.assert_no_active_block_jobs()
|
|
|
|
result = self.vm.qmp('blockdev-mirror', job_id='drive0', device='drive0',
|
|
|
|
sync='full', target='target')
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
self.complete_and_wait()
|
|
|
|
self.assert_no_active_block_jobs()
|
|
|
|
|
|
|
|
result = self.vm.qmp('blockdev-del', node_name='drive0')
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
|
|
|
result = self.vm.qmp('blockdev-del', node_name='target')
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
|
|
|
self.assertTrue(iotests.compare_images(test_img, target_img),
|
|
|
|
'target image does not match source after mirroring')
|
|
|
|
|
2016-06-22 21:51:04 +02:00
|
|
|
class TestGranularity(iotests.QMPTestCase):
|
|
|
|
image_len = 10 * 1024 * 1024 # MB
|
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
qemu_img('create', '-f', iotests.imgfmt, test_img,
|
|
|
|
str(TestGranularity.image_len))
|
|
|
|
qemu_io('-c', 'write 0 %d' % (self.image_len),
|
|
|
|
test_img)
|
|
|
|
self.vm = iotests.VM().add_drive(test_img)
|
|
|
|
self.vm.launch()
|
|
|
|
|
|
|
|
def tearDown(self):
|
|
|
|
self.vm.shutdown()
|
|
|
|
self.assertTrue(iotests.compare_images(test_img, target_img),
|
|
|
|
'target image does not match source after mirroring')
|
|
|
|
os.remove(test_img)
|
|
|
|
os.remove(target_img)
|
|
|
|
|
|
|
|
def test_granularity(self):
|
|
|
|
self.assert_no_active_block_jobs()
|
|
|
|
result = self.vm.qmp('drive-mirror', device='drive0',
|
|
|
|
sync='full', target=target_img,
|
|
|
|
mode='absolute-paths', granularity=8192)
|
|
|
|
self.assert_qmp(result, 'return', {})
|
2018-04-30 19:09:46 +02:00
|
|
|
|
2016-06-22 21:51:04 +02:00
|
|
|
event = self.vm.get_qmp_event(wait=60.0)
|
2018-04-30 19:09:46 +02:00
|
|
|
while event['event'] == 'JOB_STATUS_CHANGE':
|
|
|
|
self.assert_qmp(event, 'data/id', 'drive0')
|
|
|
|
event = self.vm.get_qmp_event(wait=60.0)
|
|
|
|
|
2016-06-22 21:51:04 +02:00
|
|
|
# Failures will manifest as COMPLETED/ERROR.
|
|
|
|
self.assert_qmp(event, 'event', 'BLOCK_JOB_READY')
|
|
|
|
self.complete_and_wait(drive='drive0', wait_ready=False)
|
|
|
|
self.assert_no_active_block_jobs()
|
|
|
|
|
2015-06-08 07:56:12 +02:00
|
|
|
class TestRepairQuorum(iotests.QMPTestCase):
|
2014-06-27 18:25:26 +02:00
|
|
|
""" This class test quorum file repair using drive-mirror.
|
|
|
|
It's mostly a fork of TestSingleDrive """
|
|
|
|
image_len = 1 * 1024 * 1024 # MB
|
|
|
|
IMAGES = [ quorum_img1, quorum_img2, quorum_img3 ]
|
|
|
|
|
2020-01-14 15:02:03 +01:00
|
|
|
@iotests.skip_if_unsupported(['quorum'])
|
2014-06-27 18:25:26 +02:00
|
|
|
def setUp(self):
|
|
|
|
self.vm = iotests.VM()
|
|
|
|
|
2015-09-02 20:52:26 +02:00
|
|
|
if iotests.qemu_default_machine == 'pc':
|
|
|
|
self.vm.add_drive(None, 'media=cdrom', 'ide')
|
|
|
|
|
2014-06-27 18:25:26 +02:00
|
|
|
# Add each individual quorum images
|
|
|
|
for i in self.IMAGES:
|
|
|
|
qemu_img('create', '-f', iotests.imgfmt, i,
|
2020-02-18 11:34:52 +01:00
|
|
|
str(self.image_len))
|
2014-06-27 18:25:26 +02:00
|
|
|
# Assign a node name to each quorum image in order to manipulate
|
|
|
|
# them
|
|
|
|
opts = "node-name=img%i" % self.IMAGES.index(i)
|
2020-02-18 11:34:39 +01:00
|
|
|
opts += ',driver=%s' % iotests.imgfmt
|
|
|
|
opts += ',file.driver=file'
|
|
|
|
opts += ',file.filename=%s' % i
|
|
|
|
self.vm = self.vm.add_blockdev(opts)
|
2014-06-27 18:25:26 +02:00
|
|
|
|
|
|
|
self.vm.launch()
|
|
|
|
|
|
|
|
#assemble the quorum block device from the individual files
|
2016-10-07 17:05:04 +02:00
|
|
|
args = { "driver": "quorum", "node-name": "quorum0",
|
|
|
|
"vote-threshold": 2, "children": [ "img0", "img1", "img2" ] }
|
2020-01-14 15:02:03 +01:00
|
|
|
result = self.vm.qmp("blockdev-add", **args)
|
|
|
|
self.assert_qmp(result, 'return', {})
|
2014-06-27 18:25:26 +02:00
|
|
|
|
|
|
|
|
|
|
|
def tearDown(self):
|
|
|
|
self.vm.shutdown()
|
2020-02-18 11:34:53 +01:00
|
|
|
for i in self.IMAGES + [ quorum_repair_img, quorum_snapshot_file,
|
|
|
|
nbd_sock_path ]:
|
2014-06-27 18:25:26 +02:00
|
|
|
# Do a try/except because the test may have deleted some images
|
|
|
|
try:
|
|
|
|
os.remove(i)
|
|
|
|
except OSError:
|
|
|
|
pass
|
|
|
|
|
|
|
|
def test_complete(self):
|
2016-09-21 14:56:00 +02:00
|
|
|
result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0',
|
|
|
|
sync='full', node_name="repair0", replaces="img1",
|
2014-06-27 18:25:26 +02:00
|
|
|
target=quorum_repair_img, format=iotests.imgfmt)
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
2016-09-21 14:56:00 +02:00
|
|
|
self.complete_and_wait(drive="job0")
|
2016-04-13 05:43:15 +02:00
|
|
|
self.assert_has_block_node("repair0", quorum_repair_img)
|
2020-02-18 11:34:51 +01:00
|
|
|
self.vm.assert_block_path('quorum0', '/children.1', 'repair0')
|
2014-06-27 18:25:26 +02:00
|
|
|
self.vm.shutdown()
|
|
|
|
self.assertTrue(iotests.compare_images(quorum_img2, quorum_repair_img),
|
|
|
|
'target image does not match source after mirroring')
|
|
|
|
|
|
|
|
def test_cancel(self):
|
2016-09-21 14:56:00 +02:00
|
|
|
result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0',
|
|
|
|
sync='full', node_name="repair0", replaces="img1",
|
2014-06-27 18:25:26 +02:00
|
|
|
target=quorum_repair_img, format=iotests.imgfmt)
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
2016-09-21 14:56:00 +02:00
|
|
|
self.cancel_and_wait(drive="job0", force=True)
|
2014-06-27 18:25:26 +02:00
|
|
|
# here we check that the last registered quorum file has not been
|
|
|
|
# swapped out and unref
|
2016-04-13 05:43:15 +02:00
|
|
|
self.assert_has_block_node(None, quorum_img3)
|
2014-06-27 18:25:26 +02:00
|
|
|
|
|
|
|
def test_cancel_after_ready(self):
|
2016-09-21 14:56:00 +02:00
|
|
|
result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0',
|
|
|
|
sync='full', node_name="repair0", replaces="img1",
|
2014-06-27 18:25:26 +02:00
|
|
|
target=quorum_repair_img, format=iotests.imgfmt)
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
2016-09-21 14:56:00 +02:00
|
|
|
self.wait_ready_and_cancel(drive="job0")
|
2014-06-27 18:25:26 +02:00
|
|
|
# here we check that the last registered quorum file has not been
|
|
|
|
# swapped out and unref
|
2016-04-13 05:43:15 +02:00
|
|
|
self.assert_has_block_node(None, quorum_img3)
|
2014-06-27 18:25:26 +02:00
|
|
|
self.vm.shutdown()
|
|
|
|
self.assertTrue(iotests.compare_images(quorum_img2, quorum_repair_img),
|
|
|
|
'target image does not match source after mirroring')
|
|
|
|
|
|
|
|
def test_pause(self):
|
2016-09-21 14:56:00 +02:00
|
|
|
result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0',
|
|
|
|
sync='full', node_name="repair0", replaces="img1",
|
2014-06-27 18:25:26 +02:00
|
|
|
target=quorum_repair_img, format=iotests.imgfmt)
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
2017-07-21 16:41:21 +02:00
|
|
|
self.pause_job('job0')
|
2014-06-27 18:25:26 +02:00
|
|
|
|
|
|
|
result = self.vm.qmp('query-block-jobs')
|
|
|
|
offset = self.dictpath(result, 'return[0]/offset')
|
|
|
|
|
2017-07-21 16:41:21 +02:00
|
|
|
time.sleep(0.5)
|
2014-06-27 18:25:26 +02:00
|
|
|
result = self.vm.qmp('query-block-jobs')
|
|
|
|
self.assert_qmp(result, 'return[0]/offset', offset)
|
|
|
|
|
2016-09-21 14:56:00 +02:00
|
|
|
result = self.vm.qmp('block-job-resume', device='job0')
|
2014-06-27 18:25:26 +02:00
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
2016-09-21 14:56:00 +02:00
|
|
|
self.complete_and_wait(drive="job0")
|
2014-06-27 18:25:26 +02:00
|
|
|
self.vm.shutdown()
|
|
|
|
self.assertTrue(iotests.compare_images(quorum_img2, quorum_repair_img),
|
|
|
|
'target image does not match source after mirroring')
|
|
|
|
|
|
|
|
def test_medium_not_found(self):
|
2015-07-03 09:28:48 +02:00
|
|
|
if iotests.qemu_default_machine != 'pc':
|
|
|
|
return
|
|
|
|
|
2016-09-21 14:56:00 +02:00
|
|
|
result = self.vm.qmp('drive-mirror', job_id='job0', device='drive0', # CD-ROM
|
2015-09-02 20:52:26 +02:00
|
|
|
sync='full',
|
2014-06-27 18:25:26 +02:00
|
|
|
node_name='repair0',
|
|
|
|
replaces='img1',
|
|
|
|
target=quorum_repair_img, format=iotests.imgfmt)
|
|
|
|
self.assert_qmp(result, 'error/class', 'GenericError')
|
|
|
|
|
|
|
|
def test_image_not_found(self):
|
2016-09-21 14:56:00 +02:00
|
|
|
result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0',
|
|
|
|
sync='full', node_name='repair0', replaces='img1',
|
|
|
|
mode='existing', target=quorum_repair_img,
|
|
|
|
format=iotests.imgfmt)
|
2014-06-27 18:25:26 +02:00
|
|
|
self.assert_qmp(result, 'error/class', 'GenericError')
|
|
|
|
|
|
|
|
def test_device_not_found(self):
|
2016-09-21 14:56:00 +02:00
|
|
|
result = self.vm.qmp('drive-mirror', job_id='job0',
|
|
|
|
device='nonexistent', sync='full',
|
2014-06-27 18:25:26 +02:00
|
|
|
node_name='repair0',
|
|
|
|
replaces='img1',
|
|
|
|
target=quorum_repair_img, format=iotests.imgfmt)
|
2016-06-23 14:20:24 +02:00
|
|
|
self.assert_qmp(result, 'error/class', 'GenericError')
|
2014-06-27 18:25:26 +02:00
|
|
|
|
|
|
|
def test_wrong_sync_mode(self):
|
2016-09-21 14:56:00 +02:00
|
|
|
result = self.vm.qmp('drive-mirror', device='quorum0', job_id='job0',
|
2014-06-27 18:25:26 +02:00
|
|
|
node_name='repair0',
|
|
|
|
replaces='img1',
|
|
|
|
target=quorum_repair_img, format=iotests.imgfmt)
|
|
|
|
self.assert_qmp(result, 'error/class', 'GenericError')
|
|
|
|
|
|
|
|
def test_no_node_name(self):
|
2016-09-21 14:56:00 +02:00
|
|
|
result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0',
|
|
|
|
sync='full', replaces='img1',
|
2014-06-27 18:25:26 +02:00
|
|
|
target=quorum_repair_img, format=iotests.imgfmt)
|
|
|
|
self.assert_qmp(result, 'error/class', 'GenericError')
|
|
|
|
|
2015-09-08 23:45:14 +02:00
|
|
|
def test_nonexistent_replaces(self):
|
2016-09-21 14:56:00 +02:00
|
|
|
result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0',
|
|
|
|
sync='full', node_name='repair0', replaces='img77',
|
2014-06-27 18:25:26 +02:00
|
|
|
target=quorum_repair_img, format=iotests.imgfmt)
|
|
|
|
self.assert_qmp(result, 'error/class', 'GenericError')
|
|
|
|
|
|
|
|
def test_after_a_quorum_snapshot(self):
|
|
|
|
result = self.vm.qmp('blockdev-snapshot-sync', node_name='img1',
|
|
|
|
snapshot_file=quorum_snapshot_file,
|
|
|
|
snapshot_node_name="snap1");
|
|
|
|
|
2016-09-21 14:56:00 +02:00
|
|
|
result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0',
|
|
|
|
sync='full', node_name='repair0', replaces="img1",
|
2014-06-27 18:25:26 +02:00
|
|
|
target=quorum_repair_img, format=iotests.imgfmt)
|
|
|
|
self.assert_qmp(result, 'error/class', 'GenericError')
|
|
|
|
|
2016-09-21 14:56:00 +02:00
|
|
|
result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0',
|
|
|
|
sync='full', node_name='repair0', replaces="snap1",
|
2014-06-27 18:25:26 +02:00
|
|
|
target=quorum_repair_img, format=iotests.imgfmt)
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
2016-09-21 14:56:00 +02:00
|
|
|
self.complete_and_wait('job0')
|
2016-04-13 05:43:15 +02:00
|
|
|
self.assert_has_block_node("repair0", quorum_repair_img)
|
2020-02-18 11:34:51 +01:00
|
|
|
self.vm.assert_block_path('quorum0', '/children.1', 'repair0')
|
2014-06-27 18:25:26 +02:00
|
|
|
|
2020-02-18 11:34:53 +01:00
|
|
|
def test_with_other_parent(self):
|
|
|
|
"""
|
|
|
|
Check that we cannot replace a Quorum child when it has other
|
|
|
|
parents.
|
|
|
|
"""
|
|
|
|
result = self.vm.qmp('nbd-server-start',
|
|
|
|
addr={
|
|
|
|
'type': 'unix',
|
|
|
|
'data': {'path': nbd_sock_path}
|
|
|
|
})
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
|
|
|
result = self.vm.qmp('nbd-server-add', device='img1')
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
|
|
|
result = self.vm.qmp('drive-mirror', job_id='mirror', device='quorum0',
|
|
|
|
sync='full', node_name='repair0', replaces='img1',
|
|
|
|
target=quorum_repair_img, format=iotests.imgfmt)
|
|
|
|
self.assert_qmp(result, 'error/desc',
|
|
|
|
"Cannot replace 'img1' by a node mirrored from "
|
|
|
|
"'quorum0', because it cannot be guaranteed that doing "
|
|
|
|
"so would not lead to an abrupt change of visible data")
|
|
|
|
|
|
|
|
def test_with_other_parents_after_mirror_start(self):
|
|
|
|
"""
|
|
|
|
The same as test_with_other_parent(), but add the NBD server
|
|
|
|
only when the mirror job is already running.
|
|
|
|
"""
|
|
|
|
result = self.vm.qmp('nbd-server-start',
|
|
|
|
addr={
|
|
|
|
'type': 'unix',
|
|
|
|
'data': {'path': nbd_sock_path}
|
|
|
|
})
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
|
|
|
result = self.vm.qmp('drive-mirror', job_id='mirror', device='quorum0',
|
|
|
|
sync='full', node_name='repair0', replaces='img1',
|
|
|
|
target=quorum_repair_img, format=iotests.imgfmt)
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
|
|
|
result = self.vm.qmp('nbd-server-add', device='img1')
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
|
|
|
# The full error message goes to stderr, we will check it later
|
|
|
|
self.complete_and_wait('mirror',
|
|
|
|
completion_error='Operation not permitted')
|
|
|
|
|
|
|
|
# Should not have been replaced
|
|
|
|
self.vm.assert_block_path('quorum0', '/children.1', 'img1')
|
|
|
|
|
|
|
|
# Check the full error message now
|
|
|
|
self.vm.shutdown()
|
|
|
|
log = self.vm.get_log()
|
|
|
|
log = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', log)
|
|
|
|
log = re.sub(r'^Formatting.*\n', '', log)
|
|
|
|
log = re.sub(r'\n\[I \+\d+\.\d+\] CLOSED\n?$', '', log)
|
|
|
|
log = re.sub(r'^%s: ' % os.path.basename(iotests.qemu_prog), '', log)
|
|
|
|
|
|
|
|
self.assertEqual(log,
|
|
|
|
"Can no longer replace 'img1' by 'repair0', because " +
|
|
|
|
"it can no longer be guaranteed that doing so would " +
|
|
|
|
"not lead to an abrupt change of visible data")
|
|
|
|
|
|
|
|
|
2017-04-03 19:51:50 +02:00
|
|
|
# Test mirroring with a source that does not have any parents (not even a
|
|
|
|
# BlockBackend)
|
|
|
|
class TestOrphanedSource(iotests.QMPTestCase):
|
|
|
|
def setUp(self):
|
|
|
|
blk0 = { 'node-name': 'src',
|
|
|
|
'driver': 'null-co' }
|
|
|
|
|
|
|
|
blk1 = { 'node-name': 'dest',
|
|
|
|
'driver': 'null-co' }
|
|
|
|
|
|
|
|
blk2 = { 'node-name': 'dest-ro',
|
|
|
|
'driver': 'null-co',
|
|
|
|
'read-only': 'on' }
|
|
|
|
|
|
|
|
self.vm = iotests.VM()
|
2018-05-08 18:10:16 +02:00
|
|
|
self.vm.add_blockdev(self.vm.qmp_to_opts(blk0))
|
|
|
|
self.vm.add_blockdev(self.vm.qmp_to_opts(blk1))
|
|
|
|
self.vm.add_blockdev(self.vm.qmp_to_opts(blk2))
|
2017-04-03 19:51:50 +02:00
|
|
|
self.vm.launch()
|
|
|
|
|
|
|
|
def tearDown(self):
|
|
|
|
self.vm.shutdown()
|
|
|
|
|
|
|
|
def test_no_job_id(self):
|
|
|
|
self.assert_no_active_block_jobs()
|
|
|
|
|
|
|
|
result = self.vm.qmp('blockdev-mirror', device='src', sync='full',
|
|
|
|
target='dest')
|
|
|
|
self.assert_qmp(result, 'error/class', 'GenericError')
|
|
|
|
|
|
|
|
def test_success(self):
|
|
|
|
self.assert_no_active_block_jobs()
|
|
|
|
|
|
|
|
result = self.vm.qmp('blockdev-mirror', job_id='job', device='src',
|
|
|
|
sync='full', target='dest')
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
|
|
|
self.complete_and_wait('job')
|
|
|
|
|
|
|
|
def test_failing_permissions(self):
|
|
|
|
self.assert_no_active_block_jobs()
|
|
|
|
|
|
|
|
result = self.vm.qmp('blockdev-mirror', device='src', sync='full',
|
|
|
|
target='dest-ro')
|
|
|
|
self.assert_qmp(result, 'error/class', 'GenericError')
|
|
|
|
|
2019-11-08 13:34:55 +01:00
|
|
|
def test_failing_permission_in_complete(self):
|
|
|
|
self.assert_no_active_block_jobs()
|
|
|
|
|
|
|
|
# Unshare consistent-read on the target
|
|
|
|
# (The mirror job does not care)
|
|
|
|
result = self.vm.qmp('blockdev-add',
|
|
|
|
driver='blkdebug',
|
|
|
|
node_name='dest-perm',
|
|
|
|
image='dest',
|
|
|
|
unshare_child_perms=['consistent-read'])
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
|
|
|
result = self.vm.qmp('blockdev-mirror', job_id='job', device='src',
|
|
|
|
sync='full', target='dest',
|
|
|
|
filter_node_name='mirror-filter')
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
|
|
|
# Require consistent-read on the source
|
|
|
|
# (We can only add this node once the job has started, or it
|
|
|
|
# will complain that it does not want to run on non-root nodes)
|
|
|
|
result = self.vm.qmp('blockdev-add',
|
|
|
|
driver='blkdebug',
|
|
|
|
node_name='src-perm',
|
|
|
|
image='src',
|
|
|
|
take_child_perms=['consistent-read'])
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
|
|
|
# While completing, mirror will attempt to replace src by
|
|
|
|
# dest, which must fail because src-perm requires
|
|
|
|
# consistent-read but dest-perm does not share it; thus
|
|
|
|
# aborting the job when it is supposed to complete
|
|
|
|
self.complete_and_wait('job',
|
|
|
|
completion_error='Operation not permitted')
|
|
|
|
|
|
|
|
# Assert that all of our nodes are still there (except for the
|
|
|
|
# mirror filter, which should be gone despite the failure)
|
|
|
|
nodes = self.vm.qmp('query-named-block-nodes')['return']
|
|
|
|
nodes = [node['node-name'] for node in nodes]
|
|
|
|
|
|
|
|
for expect in ('src', 'src-perm', 'dest', 'dest-perm'):
|
|
|
|
self.assertTrue(expect in nodes, '%s disappeared' % expect)
|
|
|
|
self.assertFalse('mirror-filter' in nodes,
|
|
|
|
'Mirror filter node did not disappear')
|
|
|
|
|
2020-02-18 11:34:54 +01:00
|
|
|
# Test cases for @replaces that do not necessarily involve Quorum
|
|
|
|
class TestReplaces(iotests.QMPTestCase):
|
|
|
|
# Each of these test cases needs their own block graph, so do not
|
|
|
|
# create any nodes here
|
|
|
|
def setUp(self):
|
|
|
|
self.vm = iotests.VM()
|
|
|
|
self.vm.launch()
|
|
|
|
|
|
|
|
def tearDown(self):
|
|
|
|
self.vm.shutdown()
|
|
|
|
for img in (test_img, target_img):
|
|
|
|
try:
|
|
|
|
os.remove(img)
|
|
|
|
except OSError:
|
|
|
|
pass
|
|
|
|
|
|
|
|
@iotests.skip_if_unsupported(['copy-on-read'])
|
|
|
|
def test_replace_filter(self):
|
|
|
|
"""
|
|
|
|
Check that we can replace filter nodes.
|
|
|
|
"""
|
|
|
|
result = self.vm.qmp('blockdev-add', **{
|
|
|
|
'driver': 'copy-on-read',
|
|
|
|
'node-name': 'filter0',
|
|
|
|
'file': {
|
|
|
|
'driver': 'copy-on-read',
|
|
|
|
'node-name': 'filter1',
|
|
|
|
'file': {
|
|
|
|
'driver': 'null-co'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
|
|
|
result = self.vm.qmp('blockdev-add',
|
|
|
|
node_name='target', driver='null-co')
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
|
|
|
result = self.vm.qmp('blockdev-mirror', job_id='mirror', device='filter0',
|
|
|
|
target='target', sync='full', replaces='filter1')
|
|
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
|
|
|
|
self.complete_and_wait('mirror')
|
|
|
|
|
|
|
|
self.vm.assert_block_path('filter0', '/file', 'target')
|
|
|
|
|
2012-10-23 16:39:22 +02:00
|
|
|
if __name__ == '__main__':
|
2019-09-02 21:33:18 +02:00
|
|
|
iotests.main(supported_fmts=['qcow2', 'qed'],
|
2020-01-21 10:52:01 +01:00
|
|
|
supported_protocols=['file'],
|
|
|
|
supported_platforms=['linux', 'freebsd', 'netbsd', 'openbsd'])
|