Browse Source
-----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQEcBAABCAAGBQJXYqffAAoJEDuxQgLoOKytiuoH+wWkxRsuRvuNZf2feQOyyznd XJdycJKNnJp5PscryaHqJzc1tAapEKDE257URYkXI+hF7Vue1r6jNrfgfR6AXysK gVfJ0BbELYWly7ID04Q8C9P1RUmEjbYqQRnB7nua33wq9P/92RIR373p/kGVJBix RM4e+xYfvGYOgNODF9jJKw4R5Sw2ZVmchWlwjcYjyRW8gOiS8OaFwX7FIB3+kj+P ew4hsZkZmK8uroMmfC3Oe5iZfvLXzKBaMT89XiL6lUXhDizYvSkPOJoIyLrfeQ3e 5AAv0AnQhrSfG2YNjOA3SsFiIIUEjLf8jr05Cr0YLXqr4OHk3Zoc7vsKDnY3ai8= =QRX6 -----END PGP SIGNATURE----- Merge remote-tracking branch 'mreitz/tags/pull-block-for-kevin-2016-06-16' into queue-block Block patches # gpg: Signature made Thu Jun 16 15:21:35 2016 CEST # gpg: using RSA key 0x3BB14202E838ACAD # gpg: Good signature from "Max Reitz <mreitz@redhat.com>" # Primary key fingerprint: 91BE B60A 30DB 3E88 57D1 1829 F407 DB00 61D5 CF40 # Subkey fingerprint: 58B3 81CE 2DC8 9CF9 9730 EE64 3BB1 4202 E838 ACAD * mreitz/tags/pull-block-for-kevin-2016-06-16: hbitmap: add 'pos < size' asserts iotests: Add test for oVirt-like storage migration iotests: Add test for post-mirror backing chains block/null: Implement bdrv_refresh_filename() block/mirror: Fix target backing BDS block: Allow replacement of a BDS by its overlay rbd:change error_setg() to error_setg_errno() iotests: 095: Clean up QEMU before showing image info block: Create the commit block job before reopening any image block: Prevent sleeping jobs from resuming if they have been paused block: use the block job list in qmp_query_block_jobs() block: use the block job list in bdrv_drain_all() Signed-off-by: Kevin Wolf <kwolf@redhat.com>pull/42/head
16 changed files with 646 additions and 64 deletions
@ -0,0 +1,261 @@ |
|||
#!/usr/bin/env python |
|||
# |
|||
# Test whether the backing BDSs are correct after completion of a |
|||
# mirror block job; in "existing" modes (drive-mirror with |
|||
# mode=existing and blockdev-mirror) the backing chain should not be |
|||
# overridden. |
|||
# |
|||
# Copyright (C) 2016 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 os |
|||
import iotests |
|||
from iotests import qemu_img |
|||
|
|||
back0_img = os.path.join(iotests.test_dir, 'back0.' + iotests.imgfmt) |
|||
back1_img = os.path.join(iotests.test_dir, 'back1.' + iotests.imgfmt) |
|||
back2_img = os.path.join(iotests.test_dir, 'back2.' + iotests.imgfmt) |
|||
source_img = os.path.join(iotests.test_dir, 'source.' + iotests.imgfmt) |
|||
target_img = os.path.join(iotests.test_dir, 'target.' + iotests.imgfmt) |
|||
|
|||
|
|||
# Class variables for controlling its behavior: |
|||
# |
|||
# existing: If True, explicitly create the target image and blockdev-add it |
|||
# target_backing: If existing is True: Use this filename as the backing file |
|||
# of the target image |
|||
# (None: no backing file) |
|||
# target_blockdev_backing: If existing is True: Pass this dict as "backing" |
|||
# for the blockdev-add command |
|||
# (None: do not pass "backing") |
|||
# target_real_backing: If existing is True: The real filename of the backing |
|||
# image during runtime, only makes sense if |
|||
# target_blockdev_backing is not None |
|||
# (None: same as target_backing) |
|||
|
|||
class BaseClass(iotests.QMPTestCase): |
|||
target_blockdev_backing = None |
|||
target_real_backing = None |
|||
|
|||
def setUp(self): |
|||
qemu_img('create', '-f', iotests.imgfmt, back0_img, '1M') |
|||
qemu_img('create', '-f', iotests.imgfmt, '-b', back0_img, back1_img) |
|||
qemu_img('create', '-f', iotests.imgfmt, '-b', back1_img, back2_img) |
|||
qemu_img('create', '-f', iotests.imgfmt, '-b', back2_img, source_img) |
|||
|
|||
self.vm = iotests.VM() |
|||
self.vm.add_drive(None, '', 'none') |
|||
self.vm.launch() |
|||
|
|||
# Add the BDS via blockdev-add so it stays around after the mirror block |
|||
# job has been completed |
|||
result = self.vm.qmp('blockdev-add', |
|||
options={'node-name': 'source', |
|||
'driver': iotests.imgfmt, |
|||
'file': {'driver': 'file', |
|||
'filename': source_img}}) |
|||
self.assert_qmp(result, 'return', {}) |
|||
|
|||
result = self.vm.qmp('x-blockdev-insert-medium', |
|||
device='drive0', node_name='source') |
|||
self.assert_qmp(result, 'return', {}) |
|||
|
|||
self.assertIntactSourceBackingChain() |
|||
|
|||
if self.existing: |
|||
if self.target_backing: |
|||
qemu_img('create', '-f', iotests.imgfmt, |
|||
'-b', self.target_backing, target_img, '1M') |
|||
else: |
|||
qemu_img('create', '-f', iotests.imgfmt, target_img, '1M') |
|||
|
|||
if self.cmd == 'blockdev-mirror': |
|||
options = { 'node-name': 'target', |
|||
'driver': iotests.imgfmt, |
|||
'file': { 'driver': 'file', |
|||
'filename': target_img } } |
|||
if self.target_blockdev_backing: |
|||
options['backing'] = self.target_blockdev_backing |
|||
|
|||
result = self.vm.qmp('blockdev-add', options=options) |
|||
self.assert_qmp(result, 'return', {}) |
|||
|
|||
def tearDown(self): |
|||
self.vm.shutdown() |
|||
os.remove(source_img) |
|||
os.remove(back2_img) |
|||
os.remove(back1_img) |
|||
os.remove(back0_img) |
|||
try: |
|||
os.remove(target_img) |
|||
except OSError: |
|||
pass |
|||
|
|||
def findBlockNode(self, node_name, id=None): |
|||
if id: |
|||
result = self.vm.qmp('query-block') |
|||
for device in result['return']: |
|||
if device['device'] == id: |
|||
if node_name: |
|||
self.assert_qmp(device, 'inserted/node-name', node_name) |
|||
return device['inserted'] |
|||
else: |
|||
result = self.vm.qmp('query-named-block-nodes') |
|||
for node in result['return']: |
|||
if node['node-name'] == node_name: |
|||
return node |
|||
|
|||
self.fail('Cannot find node %s/%s' % (id, node_name)) |
|||
|
|||
def assertIntactSourceBackingChain(self): |
|||
node = self.findBlockNode('source') |
|||
|
|||
self.assert_qmp(node, 'image' + '/backing-image' * 0 + '/filename', |
|||
source_img) |
|||
self.assert_qmp(node, 'image' + '/backing-image' * 1 + '/filename', |
|||
back2_img) |
|||
self.assert_qmp(node, 'image' + '/backing-image' * 2 + '/filename', |
|||
back1_img) |
|||
self.assert_qmp(node, 'image' + '/backing-image' * 3 + '/filename', |
|||
back0_img) |
|||
self.assert_qmp_absent(node, 'image' + '/backing-image' * 4) |
|||
|
|||
def assertCorrectBackingImage(self, node, default_image): |
|||
if self.existing: |
|||
if self.target_real_backing: |
|||
image = self.target_real_backing |
|||
else: |
|||
image = self.target_backing |
|||
else: |
|||
image = default_image |
|||
|
|||
if image: |
|||
self.assert_qmp(node, 'image/backing-image/filename', image) |
|||
else: |
|||
self.assert_qmp_absent(node, 'image/backing-image') |
|||
|
|||
|
|||
# Class variables for controlling its behavior: |
|||
# |
|||
# cmd: Mirroring command to execute, either drive-mirror or blockdev-mirror |
|||
|
|||
class MirrorBaseClass(BaseClass): |
|||
def runMirror(self, sync): |
|||
if self.cmd == 'blockdev-mirror': |
|||
result = self.vm.qmp(self.cmd, device='drive0', sync=sync, |
|||
target='target') |
|||
else: |
|||
if self.existing: |
|||
mode = 'existing' |
|||
else: |
|||
mode = 'absolute-paths' |
|||
result = self.vm.qmp(self.cmd, device='drive0', sync=sync, |
|||
target=target_img, format=iotests.imgfmt, |
|||
mode=mode, node_name='target') |
|||
|
|||
self.assert_qmp(result, 'return', {}) |
|||
|
|||
self.vm.event_wait('BLOCK_JOB_READY') |
|||
|
|||
result = self.vm.qmp('block-job-complete', device='drive0') |
|||
self.assert_qmp(result, 'return', {}) |
|||
|
|||
self.vm.event_wait('BLOCK_JOB_COMPLETED') |
|||
|
|||
def testFull(self): |
|||
self.runMirror('full') |
|||
|
|||
node = self.findBlockNode('target', 'drive0') |
|||
self.assertCorrectBackingImage(node, None) |
|||
self.assertIntactSourceBackingChain() |
|||
|
|||
def testTop(self): |
|||
self.runMirror('top') |
|||
|
|||
node = self.findBlockNode('target', 'drive0') |
|||
self.assertCorrectBackingImage(node, back2_img) |
|||
self.assertIntactSourceBackingChain() |
|||
|
|||
def testNone(self): |
|||
self.runMirror('none') |
|||
|
|||
node = self.findBlockNode('target', 'drive0') |
|||
self.assertCorrectBackingImage(node, source_img) |
|||
self.assertIntactSourceBackingChain() |
|||
|
|||
|
|||
class TestDriveMirrorAbsolutePaths(MirrorBaseClass): |
|||
cmd = 'drive-mirror' |
|||
existing = False |
|||
|
|||
class TestDriveMirrorExistingNoBacking(MirrorBaseClass): |
|||
cmd = 'drive-mirror' |
|||
existing = True |
|||
target_backing = None |
|||
|
|||
class TestDriveMirrorExistingBacking(MirrorBaseClass): |
|||
cmd = 'drive-mirror' |
|||
existing = True |
|||
target_backing = 'null-co://' |
|||
|
|||
class TestBlockdevMirrorNoBacking(MirrorBaseClass): |
|||
cmd = 'blockdev-mirror' |
|||
existing = True |
|||
target_backing = None |
|||
|
|||
class TestBlockdevMirrorBacking(MirrorBaseClass): |
|||
cmd = 'blockdev-mirror' |
|||
existing = True |
|||
target_backing = 'null-co://' |
|||
|
|||
class TestBlockdevMirrorForcedBacking(MirrorBaseClass): |
|||
cmd = 'blockdev-mirror' |
|||
existing = True |
|||
target_backing = None |
|||
target_blockdev_backing = { 'driver': 'null-co' } |
|||
target_real_backing = 'null-co://' |
|||
|
|||
|
|||
class TestCommit(BaseClass): |
|||
existing = False |
|||
|
|||
def testCommit(self): |
|||
result = self.vm.qmp('block-commit', device='drive0', base=back1_img) |
|||
self.assert_qmp(result, 'return', {}) |
|||
|
|||
self.vm.event_wait('BLOCK_JOB_READY') |
|||
|
|||
result = self.vm.qmp('block-job-complete', device='drive0') |
|||
self.assert_qmp(result, 'return', {}) |
|||
|
|||
self.vm.event_wait('BLOCK_JOB_COMPLETED') |
|||
|
|||
node = self.findBlockNode(None, 'drive0') |
|||
self.assert_qmp(node, 'image' + '/backing-image' * 0 + '/filename', |
|||
back1_img) |
|||
self.assert_qmp(node, 'image' + '/backing-image' * 1 + '/filename', |
|||
back0_img) |
|||
self.assert_qmp_absent(node, 'image' + '/backing-image' * 2 + |
|||
'/filename') |
|||
|
|||
self.assertIntactSourceBackingChain() |
|||
|
|||
|
|||
BaseClass = None |
|||
MirrorBaseClass = None |
|||
|
|||
if __name__ == '__main__': |
|||
iotests.main(supported_fmts=['qcow2']) |
|||
@ -0,0 +1,5 @@ |
|||
................... |
|||
---------------------------------------------------------------------- |
|||
Ran 19 tests |
|||
|
|||
OK |
|||
@ -0,0 +1,174 @@ |
|||
#!/bin/bash |
|||
# |
|||
# Tests oVirt-like storage migration: |
|||
# - Create snapshot |
|||
# - Create target image with (not yet existing) target backing chain |
|||
# (i.e. just write the name of a soon-to-be-copied-over backing file into it) |
|||
# - drive-mirror the snapshot to the target with mode=existing and sync=top |
|||
# - In the meantime, copy the original source files to the destination via |
|||
# conventional means (i.e. outside of qemu) |
|||
# - Complete the drive-mirror job |
|||
# - Delete all source images |
|||
# |
|||
# Copyright (C) 2016 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/>. |
|||
# |
|||
|
|||
# creator |
|||
owner=mreitz@redhat.com |
|||
|
|||
seq="$(basename $0)" |
|||
echo "QA output created by $seq" |
|||
|
|||
here="$PWD" |
|||
status=1 # failure is the default! |
|||
|
|||
_cleanup() |
|||
{ |
|||
rm -f "$TEST_IMG{,.target}{,.backing,.overlay}" |
|||
} |
|||
trap "_cleanup; exit \$status" 0 1 2 3 15 |
|||
|
|||
# get standard environment, filters and checks |
|||
. ./common.rc |
|||
. ./common.filter |
|||
. ./common.qemu |
|||
|
|||
_supported_fmt qcow2 qed |
|||
_supported_proto generic |
|||
_supported_os Linux |
|||
|
|||
# Create source disk |
|||
TEST_IMG="$TEST_IMG.backing" _make_test_img 1M |
|||
_make_test_img -b "$TEST_IMG.backing" 1M |
|||
|
|||
$QEMU_IO -c 'write -P 1 0 256k' "$TEST_IMG.backing" | _filter_qemu_io |
|||
$QEMU_IO -c 'write -P 2 64k 192k' "$TEST_IMG" | _filter_qemu_io |
|||
|
|||
_launch_qemu -drive if=none,id=source,file="$TEST_IMG" |
|||
|
|||
_send_qemu_cmd $QEMU_HANDLE \ |
|||
"{ 'execute': 'qmp_capabilities' }" \ |
|||
'return' |
|||
|
|||
# Create snapshot |
|||
TEST_IMG="$TEST_IMG.overlay" _make_test_img -b "$TEST_IMG" 1M |
|||
_send_qemu_cmd $QEMU_HANDLE \ |
|||
"{ 'execute': 'blockdev-snapshot-sync', |
|||
'arguments': { 'device': 'source', |
|||
'snapshot-file': '$TEST_IMG.overlay', |
|||
'format': '$IMGFMT', |
|||
'mode': 'existing' } }" \ |
|||
'return' |
|||
|
|||
# Write something to the snapshot |
|||
_send_qemu_cmd $QEMU_HANDLE \ |
|||
"{ 'execute': 'human-monitor-command', |
|||
'arguments': { 'command-line': |
|||
'qemu-io source \"write -P 3 128k 128k\"' } }" \ |
|||
'return' |
|||
|
|||
# Create target image |
|||
TEST_IMG="$TEST_IMG.target.overlay" _make_test_img -b "$TEST_IMG.target" 1M |
|||
|
|||
# Mirror snapshot |
|||
_send_qemu_cmd $QEMU_HANDLE \ |
|||
"{ 'execute': 'drive-mirror', |
|||
'arguments': { 'device': 'source', |
|||
'target': '$TEST_IMG.target.overlay', |
|||
'mode': 'existing', |
|||
'sync': 'top' } }" \ |
|||
'return' |
|||
|
|||
# Wait for convergence |
|||
_send_qemu_cmd $QEMU_HANDLE \ |
|||
'' \ |
|||
'BLOCK_JOB_READY' |
|||
|
|||
# Write some more |
|||
_send_qemu_cmd $QEMU_HANDLE \ |
|||
"{ 'execute': 'human-monitor-command', |
|||
'arguments': { 'command-line': |
|||
'qemu-io source \"write -P 4 192k 64k\"' } }" \ |
|||
'return' |
|||
|
|||
# Copy source backing chain to the target before completing the job |
|||
cp "$TEST_IMG.backing" "$TEST_IMG.target.backing" |
|||
cp "$TEST_IMG" "$TEST_IMG.target" |
|||
$QEMU_IMG rebase -u -b "$TEST_IMG.target.backing" "$TEST_IMG.target" |
|||
|
|||
# Complete block job |
|||
_send_qemu_cmd $QEMU_HANDLE \ |
|||
"{ 'execute': 'block-job-complete', |
|||
'arguments': { 'device': 'source' } }" \ |
|||
'' |
|||
|
|||
_send_qemu_cmd $QEMU_HANDLE \ |
|||
'' \ |
|||
'BLOCK_JOB_COMPLETED' |
|||
|
|||
# Remove the source images |
|||
rm -f "$TEST_IMG{,.backing,.overlay}" |
|||
|
|||
echo |
|||
|
|||
# Check online disk contents |
|||
_send_qemu_cmd $QEMU_HANDLE \ |
|||
"{ 'execute': 'human-monitor-command', |
|||
'arguments': { 'command-line': |
|||
'qemu-io source \"read -P 1 0k 64k\"' } }" \ |
|||
'return' |
|||
|
|||
_send_qemu_cmd $QEMU_HANDLE \ |
|||
"{ 'execute': 'human-monitor-command', |
|||
'arguments': { 'command-line': |
|||
'qemu-io source \"read -P 2 64k 64k\"' } }" \ |
|||
'return' |
|||
|
|||
_send_qemu_cmd $QEMU_HANDLE \ |
|||
"{ 'execute': 'human-monitor-command', |
|||
'arguments': { 'command-line': |
|||
'qemu-io source \"read -P 3 128k 64k\"' } }" \ |
|||
'return' |
|||
|
|||
_send_qemu_cmd $QEMU_HANDLE \ |
|||
"{ 'execute': 'human-monitor-command', |
|||
'arguments': { 'command-line': |
|||
'qemu-io source \"read -P 4 192k 64k\"' } }" \ |
|||
'return' |
|||
|
|||
echo |
|||
|
|||
_send_qemu_cmd $QEMU_HANDLE \ |
|||
"{ 'execute': 'quit' }" \ |
|||
'return' |
|||
|
|||
wait=1 _cleanup_qemu |
|||
|
|||
echo |
|||
|
|||
# Check offline disk contents |
|||
$QEMU_IO -c 'read -P 1 0k 64k' \ |
|||
-c 'read -P 2 64k 64k' \ |
|||
-c 'read -P 3 128k 64k' \ |
|||
-c 'read -P 4 192k 64k' \ |
|||
"$TEST_IMG.target.overlay" | _filter_qemu_io |
|||
|
|||
echo |
|||
|
|||
# success, all done |
|||
echo '*** done' |
|||
rm -f $seq.full |
|||
status=0 |
|||
@ -0,0 +1,48 @@ |
|||
QA output created by 156 |
|||
Formatting 'TEST_DIR/t.IMGFMT.backing', fmt=IMGFMT size=1048576 |
|||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.backing |
|||
wrote 262144/262144 bytes at offset 0 |
|||
256 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) |
|||
wrote 196608/196608 bytes at offset 65536 |
|||
192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) |
|||
{"return": {}} |
|||
Formatting 'TEST_DIR/t.IMGFMT.overlay', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT |
|||
{"return": {}} |
|||
wrote 131072/131072 bytes at offset 131072 |
|||
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) |
|||
{"return": ""} |
|||
Formatting 'TEST_DIR/t.IMGFMT.target.overlay', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.target |
|||
{"return": {}} |
|||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "source", "len": 131072, "offset": 131072, "speed": 0, "type": "mirror"}} |
|||
wrote 65536/65536 bytes at offset 196608 |
|||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) |
|||
{"return": ""} |
|||
{"return": {}} |
|||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "source", "len": 196608, "offset": 196608, "speed": 0, "type": "mirror"}} |
|||
|
|||
read 65536/65536 bytes at offset 0 |
|||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) |
|||
{"return": ""} |
|||
read 65536/65536 bytes at offset 65536 |
|||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) |
|||
{"return": ""} |
|||
read 65536/65536 bytes at offset 131072 |
|||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) |
|||
{"return": ""} |
|||
read 65536/65536 bytes at offset 196608 |
|||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) |
|||
{"return": ""} |
|||
|
|||
{"return": {}} |
|||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"} |
|||
|
|||
read 65536/65536 bytes at offset 0 |
|||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) |
|||
read 65536/65536 bytes at offset 65536 |
|||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) |
|||
read 65536/65536 bytes at offset 131072 |
|||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) |
|||
read 65536/65536 bytes at offset 196608 |
|||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) |
|||
|
|||
*** done |
|||
Loading…
Reference in new issue