* [Qemu-devel] [PULL for-2.0 0/6] Block patches
@ 2014-03-14 16:14 Stefan Hajnoczi
2014-03-14 16:14 ` [Qemu-devel] [PULL for-2.0 1/6] blockdev: Refuse to open encrypted image unless paused Stefan Hajnoczi
` (6 more replies)
0 siblings, 7 replies; 8+ messages in thread
From: Stefan Hajnoczi @ 2014-03-14 16:14 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Maydell, Stefan Hajnoczi, Anthony Liguori
Bug fixes for the next QEMU 2.0 release candidate.
The following changes since commit 5d92c74f8a1728a202ba9457872ab0f27ff15e81:
Update version for v2.0-rc0 (2014-03-13 20:08:15 -0700)
are available in the git repository at:
git@github.com:stefanha/qemu.git tags/block-pull-request
for you to fetch changes up to 46dea4160d587add2f3670306c41ad9ad4064af5:
qemu-iotests: remove 085 and 087 from 'quick' group (2014-03-14 16:30:38 +0100)
----------------------------------------------------------------
Block pull request
----------------------------------------------------------------
Jeff Cody (1):
block: Explicitly specify 'unsigned long long' for VHDX 64-bit constants
Markus Armbruster (1):
blockdev: Refuse to open encrypted image unless paused
Stefan Hajnoczi (4):
nbd: close socket if connection breaks
tests: add nbd-fault-injector.py utility
qemu-iotests: add 083 NBD client disconnect tests
qemu-iotests: remove 085 and 087 from 'quick' group
block.c | 9 +-
block/nbd-client.c | 33 ++--
block/vhdx.h | 6 +-
stubs/Makefile.objs | 1 +
stubs/runstate-check.c | 6 +
tests/qemu-iotests/083 | 129 +++++++++++++++
tests/qemu-iotests/083.out | 163 +++++++++++++++++++
tests/qemu-iotests/087 | 17 ++
tests/qemu-iotests/087.out | 11 +-
tests/qemu-iotests/group | 5 +-
tests/qemu-iotests/nbd-fault-injector.py | 264 +++++++++++++++++++++++++++++++
11 files changed, 622 insertions(+), 22 deletions(-)
create mode 100644 stubs/runstate-check.c
create mode 100755 tests/qemu-iotests/083
create mode 100644 tests/qemu-iotests/083.out
create mode 100755 tests/qemu-iotests/nbd-fault-injector.py
--
1.8.5.3
^ permalink raw reply [flat|nested] 8+ messages in thread
* [Qemu-devel] [PULL for-2.0 1/6] blockdev: Refuse to open encrypted image unless paused
2014-03-14 16:14 [Qemu-devel] [PULL for-2.0 0/6] Block patches Stefan Hajnoczi
@ 2014-03-14 16:14 ` Stefan Hajnoczi
2014-03-14 16:14 ` [Qemu-devel] [PULL for-2.0 2/6] block: Explicitly specify 'unsigned long long' for VHDX 64-bit constants Stefan Hajnoczi
` (5 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Stefan Hajnoczi @ 2014-03-14 16:14 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Stefan Hajnoczi, Markus Armbruster,
Anthony Liguori
From: Markus Armbruster <armbru@redhat.com>
Opening an encrypted image takes an additional step: setting the key.
Between open and the key set, the image must not be used.
We have some protection against accidental use in place: you can't
unpause a guest while we're missing keys. You can, however, hot-plug
block devices lacking keys into a running guest just fine, or insert
media lacking keys. In the latter case, notifying the guest of the
insert is delayed until the key is set, which may suffice to protect
at least some guests in common usage.
This patch makes the protection apply in more cases, in a rather
heavy-handed way: it doesn't let you open encrypted images unless
we're in a paused state.
It doesn't extend the protection to users other than the guest (block
jobs?). Use of runstate_check() from block.c is disgusting. Best I
can do right now.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
block.c | 9 ++++++++-
stubs/Makefile.objs | 1 +
stubs/runstate-check.c | 6 ++++++
tests/qemu-iotests/087 | 17 +++++++++++++++++
tests/qemu-iotests/087.out | 11 ++++++++++-
5 files changed, 42 insertions(+), 2 deletions(-)
create mode 100644 stubs/runstate-check.c
diff --git a/block.c b/block.c
index fae50c9..53f5b44 100644
--- a/block.c
+++ b/block.c
@@ -1388,12 +1388,19 @@ done:
ret = -EINVAL;
goto close_and_fail;
}
- QDECREF(options);
if (!bdrv_key_required(bs)) {
bdrv_dev_change_media_cb(bs, true);
+ } else if (!runstate_check(RUN_STATE_PRELAUNCH)
+ && !runstate_check(RUN_STATE_INMIGRATE)
+ && !runstate_check(RUN_STATE_PAUSED)) { /* HACK */
+ error_setg(errp,
+ "Guest must be stopped for opening of encrypted image");
+ ret = -EBUSY;
+ goto close_and_fail;
}
+ QDECREF(options);
*pbs = bs;
return 0;
diff --git a/stubs/Makefile.objs b/stubs/Makefile.objs
index 59c5a54..5ed1d38 100644
--- a/stubs/Makefile.objs
+++ b/stubs/Makefile.objs
@@ -20,6 +20,7 @@ stub-obj-y += mon-set-error.o
stub-obj-y += pci-drive-hot-add.o
stub-obj-y += qtest.o
stub-obj-y += reset.o
+stub-obj-y += runstate-check.o
stub-obj-y += set-fd-handler.o
stub-obj-y += slirp.o
stub-obj-y += sysbus.o
diff --git a/stubs/runstate-check.c b/stubs/runstate-check.c
new file mode 100644
index 0000000..bd2e375
--- /dev/null
+++ b/stubs/runstate-check.c
@@ -0,0 +1,6 @@
+#include "sysemu/sysemu.h"
+
+bool runstate_check(RunState state)
+{
+ return state == RUN_STATE_PRELAUNCH;
+}
diff --git a/tests/qemu-iotests/087 b/tests/qemu-iotests/087
index 53b6c43..a38bb70 100755
--- a/tests/qemu-iotests/087
+++ b/tests/qemu-iotests/087
@@ -99,6 +99,23 @@ echo === Encrypted image ===
echo
_make_test_img -o encryption=on $size
+run_qemu -S <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "blockdev-add",
+ "arguments": {
+ "options": {
+ "driver": "$IMGFMT",
+ "id": "disk",
+ "file": {
+ "driver": "file",
+ "filename": "$TEST_IMG"
+ }
+ }
+ }
+ }
+{ "execute": "quit" }
+EOF
+
run_qemu <<EOF
{ "execute": "qmp_capabilities" }
{ "execute": "blockdev-add",
diff --git a/tests/qemu-iotests/087.out b/tests/qemu-iotests/087.out
index b871032..e65dcdf 100644
--- a/tests/qemu-iotests/087.out
+++ b/tests/qemu-iotests/087.out
@@ -28,7 +28,7 @@ QMP_VERSION
=== Encrypted image ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on
-Testing:
+Testing: -S
QMP_VERSION
{"return": {}}
{"error": {"class": "GenericError", "desc": "blockdev-add doesn't support encrypted devices"}}
@@ -37,4 +37,13 @@ QMP_VERSION
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "ide1-cd0", "tray-open": true}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "floppy0", "tray-open": true}}
+Testing:
+QMP_VERSION
+{"return": {}}
+{"error": {"class": "GenericError", "desc": "could not open disk image disk: Guest must be stopped for opening of encrypted image"}}
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "ide1-cd0", "tray-open": true}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "floppy0", "tray-open": true}}
+
*** done
--
1.8.5.3
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [Qemu-devel] [PULL for-2.0 2/6] block: Explicitly specify 'unsigned long long' for VHDX 64-bit constants
2014-03-14 16:14 [Qemu-devel] [PULL for-2.0 0/6] Block patches Stefan Hajnoczi
2014-03-14 16:14 ` [Qemu-devel] [PULL for-2.0 1/6] blockdev: Refuse to open encrypted image unless paused Stefan Hajnoczi
@ 2014-03-14 16:14 ` Stefan Hajnoczi
2014-03-14 16:14 ` [Qemu-devel] [PULL for-2.0 3/6] nbd: close socket if connection breaks Stefan Hajnoczi
` (4 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Stefan Hajnoczi @ 2014-03-14 16:14 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Maydell, Jeff Cody, Stefan Hajnoczi, Anthony Liguori
From: Jeff Cody <jcody@redhat.com>
On 32-bit hosts, some compilers will warn on too large integer constants
for constants that are 64-bit in length. Explicitly put a 'ULL' suffix
on those defines.
Reported-by: Alexander Graf <agraf@suse.de>
Signed-off-by: Jeff Cody <jcody@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
block/vhdx.h | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/block/vhdx.h b/block/vhdx.h
index 2acd7c2..8103d4c 100644
--- a/block/vhdx.h
+++ b/block/vhdx.h
@@ -61,7 +61,7 @@
/* These structures are ones that are defined in the VHDX specification
* document */
-#define VHDX_FILE_SIGNATURE 0x656C696678646876 /* "vhdxfile" in ASCII */
+#define VHDX_FILE_SIGNATURE 0x656C696678646876ULL /* "vhdxfile" in ASCII */
typedef struct VHDXFileIdentifier {
uint64_t signature; /* "vhdxfile" in ASCII */
uint16_t creator[256]; /* optional; utf-16 string to identify
@@ -238,7 +238,7 @@ typedef struct QEMU_PACKED VHDXLogDataSector {
/* upper 44 bits are the file offset in 1MB units lower 3 bits are the state
other bits are reserved */
#define VHDX_BAT_STATE_BIT_MASK 0x07
-#define VHDX_BAT_FILE_OFF_MASK 0xFFFFFFFFFFF00000 /* upper 44 bits */
+#define VHDX_BAT_FILE_OFF_MASK 0xFFFFFFFFFFF00000ULL /* upper 44 bits */
typedef uint64_t VHDXBatEntry;
/* ---- METADATA REGION STRUCTURES ---- */
@@ -247,7 +247,7 @@ typedef uint64_t VHDXBatEntry;
#define VHDX_METADATA_MAX_ENTRIES 2047 /* not including the header */
#define VHDX_METADATA_TABLE_MAX_SIZE \
(VHDX_METADATA_ENTRY_SIZE * (VHDX_METADATA_MAX_ENTRIES+1))
-#define VHDX_METADATA_SIGNATURE 0x617461646174656D /* "metadata" in ASCII */
+#define VHDX_METADATA_SIGNATURE 0x617461646174656DULL /* "metadata" in ASCII */
typedef struct QEMU_PACKED VHDXMetadataTableHeader {
uint64_t signature; /* "metadata" in ASCII */
uint16_t reserved;
--
1.8.5.3
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [Qemu-devel] [PULL for-2.0 3/6] nbd: close socket if connection breaks
2014-03-14 16:14 [Qemu-devel] [PULL for-2.0 0/6] Block patches Stefan Hajnoczi
2014-03-14 16:14 ` [Qemu-devel] [PULL for-2.0 1/6] blockdev: Refuse to open encrypted image unless paused Stefan Hajnoczi
2014-03-14 16:14 ` [Qemu-devel] [PULL for-2.0 2/6] block: Explicitly specify 'unsigned long long' for VHDX 64-bit constants Stefan Hajnoczi
@ 2014-03-14 16:14 ` Stefan Hajnoczi
2014-03-14 16:14 ` [Qemu-devel] [PULL for-2.0 4/6] tests: add nbd-fault-injector.py utility Stefan Hajnoczi
` (3 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Stefan Hajnoczi @ 2014-03-14 16:14 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Maydell, Stefan Hajnoczi, Anthony Liguori
nbd_receive_reply() is called by the event loop whenever data is
available or the socket has been closed by the remote side.
This patch closes the socket when an error occurs to prevent the
nbd_receive_reply() handler from being called indefinitely after the
connection has failed.
Note that we were already correctly returning EIO for pending requests
but leaving the nbd_receive_reply() handler registered resulted in high
CPU consumption and a flood of error messages.
Reuse nbd_teardown_connection() to close the socket.
Reported-by: Zhifeng Cai <bluewindow@h3c.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
block/nbd-client.c | 33 ++++++++++++++++++---------------
1 file changed, 18 insertions(+), 15 deletions(-)
diff --git a/block/nbd-client.c b/block/nbd-client.c
index 0922b78..7d698cb 100644
--- a/block/nbd-client.c
+++ b/block/nbd-client.c
@@ -43,6 +43,17 @@ static void nbd_recv_coroutines_enter_all(NbdClientSession *s)
}
}
+static void nbd_teardown_connection(NbdClientSession *client)
+{
+ /* finish any pending coroutines */
+ shutdown(client->sock, 2);
+ nbd_recv_coroutines_enter_all(client);
+
+ qemu_aio_set_fd_handler(client->sock, NULL, NULL, NULL);
+ closesocket(client->sock);
+ client->sock = -1;
+}
+
static void nbd_reply_ready(void *opaque)
{
NbdClientSession *s = opaque;
@@ -78,7 +89,7 @@ static void nbd_reply_ready(void *opaque)
}
fail:
- nbd_recv_coroutines_enter_all(s);
+ nbd_teardown_connection(s);
}
static void nbd_restart_write(void *opaque)
@@ -324,7 +335,7 @@ int nbd_client_session_co_discard(NbdClientSession *client, int64_t sector_num,
}
-static void nbd_teardown_connection(NbdClientSession *client)
+void nbd_client_session_close(NbdClientSession *client)
{
struct nbd_request request = {
.type = NBD_CMD_DISC,
@@ -332,22 +343,14 @@ static void nbd_teardown_connection(NbdClientSession *client)
.len = 0
};
- nbd_send_request(client->sock, &request);
-
- /* finish any pending coroutines */
- shutdown(client->sock, 2);
- nbd_recv_coroutines_enter_all(client);
-
- qemu_aio_set_fd_handler(client->sock, NULL, NULL, NULL);
- closesocket(client->sock);
- client->sock = -1;
-}
-
-void nbd_client_session_close(NbdClientSession *client)
-{
if (!client->bs) {
return;
}
+ if (client->sock == -1) {
+ return;
+ }
+
+ nbd_send_request(client->sock, &request);
nbd_teardown_connection(client);
client->bs = NULL;
--
1.8.5.3
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [Qemu-devel] [PULL for-2.0 4/6] tests: add nbd-fault-injector.py utility
2014-03-14 16:14 [Qemu-devel] [PULL for-2.0 0/6] Block patches Stefan Hajnoczi
` (2 preceding siblings ...)
2014-03-14 16:14 ` [Qemu-devel] [PULL for-2.0 3/6] nbd: close socket if connection breaks Stefan Hajnoczi
@ 2014-03-14 16:14 ` Stefan Hajnoczi
2014-03-14 16:14 ` [Qemu-devel] [PULL for-2.0 5/6] qemu-iotests: add 083 NBD client disconnect tests Stefan Hajnoczi
` (2 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Stefan Hajnoczi @ 2014-03-14 16:14 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Maydell, Stefan Hajnoczi, Anthony Liguori
The nbd-fault-injector.py script is a special kind of NBD server. It
throws away all writes and produces zeroes for reads. Given a list of
fault injection rules, it can simulate NBD protocol errors and is useful
for testing NBD client error handling code paths.
See the patch for documentation. This scripts is modelled after Kevin
Wolf <kwolf@redhat.com>'s blkdebug block driver.
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
tests/qemu-iotests/nbd-fault-injector.py | 264 +++++++++++++++++++++++++++++++
1 file changed, 264 insertions(+)
create mode 100755 tests/qemu-iotests/nbd-fault-injector.py
diff --git a/tests/qemu-iotests/nbd-fault-injector.py b/tests/qemu-iotests/nbd-fault-injector.py
new file mode 100755
index 0000000..6c07191
--- /dev/null
+++ b/tests/qemu-iotests/nbd-fault-injector.py
@@ -0,0 +1,264 @@
+#!/usr/bin/env python
+# NBD server - fault injection utility
+#
+# Configuration file syntax:
+# [inject-error "disconnect-neg1"]
+# event=neg1
+# io=readwrite
+# when=before
+#
+# Note that Python's ConfigParser squashes together all sections with the same
+# name, so give each [inject-error] a unique name.
+#
+# inject-error options:
+# event - name of the trigger event
+# "neg1" - first part of negotiation struct
+# "export" - export struct
+# "neg2" - second part of negotiation struct
+# "request" - NBD request struct
+# "reply" - NBD reply struct
+# "data" - request/reply data
+# io - I/O direction that triggers this rule:
+# "read", "write", or "readwrite"
+# default: readwrite
+# when - after how many bytes to inject the fault
+# -1 - inject error after I/O
+# 0 - inject error before I/O
+# integer - inject error after integer bytes
+# "before" - alias for 0
+# "after" - alias for -1
+# default: before
+#
+# Currently the only error injection action is to terminate the server process.
+# This resets the TCP connection and thus forces the client to handle
+# unexpected connection termination.
+#
+# Other error injection actions could be added in the future.
+#
+# Copyright Red Hat, Inc. 2014
+#
+# Authors:
+# Stefan Hajnoczi <stefanha@redhat.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or later.
+# See the COPYING file in the top-level directory.
+
+import sys
+import socket
+import struct
+import collections
+import ConfigParser
+
+FAKE_DISK_SIZE = 8 * 1024 * 1024 * 1024 # 8 GB
+
+# Protocol constants
+NBD_CMD_READ = 0
+NBD_CMD_WRITE = 1
+NBD_CMD_DISC = 2
+NBD_REQUEST_MAGIC = 0x25609513
+NBD_REPLY_MAGIC = 0x67446698
+NBD_PASSWD = 0x4e42444d41474943
+NBD_OPTS_MAGIC = 0x49484156454F5054
+NBD_CLIENT_MAGIC = 0x0000420281861253
+NBD_OPT_EXPORT_NAME = 1 << 0
+
+# Protocol structs
+neg_classic_struct = struct.Struct('>QQQI124x')
+neg1_struct = struct.Struct('>QQH')
+export_tuple = collections.namedtuple('Export', 'reserved magic opt len')
+export_struct = struct.Struct('>IQII')
+neg2_struct = struct.Struct('>QH124x')
+request_tuple = collections.namedtuple('Request', 'magic type handle from_ len')
+request_struct = struct.Struct('>IIQQI')
+reply_struct = struct.Struct('>IIQ')
+
+def err(msg):
+ sys.stderr.write(msg + '\n')
+ sys.exit(1)
+
+def recvall(sock, bufsize):
+ received = 0
+ chunks = []
+ while received < bufsize:
+ chunk = sock.recv(bufsize - received)
+ if len(chunk) == 0:
+ raise Exception('unexpected disconnect')
+ chunks.append(chunk)
+ received += len(chunk)
+ return ''.join(chunks)
+
+class Rule(object):
+ def __init__(self, name, event, io, when):
+ self.name = name
+ self.event = event
+ self.io = io
+ self.when = when
+
+ def match(self, event, io):
+ if event != self.event:
+ return False
+ if io != self.io and self.io != 'readwrite':
+ return False
+ return True
+
+class FaultInjectionSocket(object):
+ def __init__(self, sock, rules):
+ self.sock = sock
+ self.rules = rules
+
+ def check(self, event, io, bufsize=None):
+ for rule in self.rules:
+ if rule.match(event, io):
+ if rule.when == 0 or bufsize is None:
+ print 'Closing connection on rule match %s' % rule.name
+ sys.exit(0)
+ if rule.when != -1:
+ return rule.when
+ return bufsize
+
+ def send(self, buf, event):
+ bufsize = self.check(event, 'write', bufsize=len(buf))
+ self.sock.sendall(buf[:bufsize])
+ self.check(event, 'write')
+
+ def recv(self, bufsize, event):
+ bufsize = self.check(event, 'read', bufsize=bufsize)
+ data = recvall(self.sock, bufsize)
+ self.check(event, 'read')
+ return data
+
+ def close(self):
+ self.sock.close()
+
+def negotiate_classic(conn):
+ buf = neg_classic_struct.pack(NBD_PASSWD, NBD_CLIENT_MAGIC,
+ FAKE_DISK_SIZE, 0)
+ conn.send(buf, event='neg-classic')
+
+def negotiate_export(conn):
+ # Send negotiation part 1
+ buf = neg1_struct.pack(NBD_PASSWD, NBD_OPTS_MAGIC, 0)
+ conn.send(buf, event='neg1')
+
+ # Receive export option
+ buf = conn.recv(export_struct.size, event='export')
+ export = export_tuple._make(export_struct.unpack(buf))
+ assert export.magic == NBD_OPTS_MAGIC
+ assert export.opt == NBD_OPT_EXPORT_NAME
+ name = conn.recv(export.len, event='export-name')
+
+ # Send negotiation part 2
+ buf = neg2_struct.pack(FAKE_DISK_SIZE, 0)
+ conn.send(buf, event='neg2')
+
+def negotiate(conn, use_export):
+ '''Negotiate export with client'''
+ if use_export:
+ negotiate_export(conn)
+ else:
+ negotiate_classic(conn)
+
+def read_request(conn):
+ '''Parse NBD request from client'''
+ buf = conn.recv(request_struct.size, event='request')
+ req = request_tuple._make(request_struct.unpack(buf))
+ assert req.magic == NBD_REQUEST_MAGIC
+ return req
+
+def write_reply(conn, error, handle):
+ buf = reply_struct.pack(NBD_REPLY_MAGIC, error, handle)
+ conn.send(buf, event='reply')
+
+def handle_connection(conn, use_export):
+ negotiate(conn, use_export)
+ while True:
+ req = read_request(conn)
+ if req.type == NBD_CMD_READ:
+ write_reply(conn, 0, req.handle)
+ conn.send('\0' * req.len, event='data')
+ elif req.type == NBD_CMD_WRITE:
+ _ = conn.recv(req.len, event='data')
+ write_reply(conn, 0, req.handle)
+ elif req.type == NBD_CMD_DISC:
+ break
+ else:
+ print 'unrecognized command type %#02x' % req.type
+ break
+ conn.close()
+
+def run_server(sock, rules, use_export):
+ while True:
+ conn, _ = sock.accept()
+ handle_connection(FaultInjectionSocket(conn, rules), use_export)
+
+def parse_inject_error(name, options):
+ if 'event' not in options:
+ err('missing \"event\" option in %s' % name)
+ event = options['event']
+ if event not in ('neg-classic', 'neg1', 'export', 'neg2', 'request', 'reply', 'data'):
+ err('invalid \"event\" option value \"%s\" in %s' % (event, name))
+ io = options.get('io', 'readwrite')
+ if io not in ('read', 'write', 'readwrite'):
+ err('invalid \"io\" option value \"%s\" in %s' % (io, name))
+ when = options.get('when', 'before')
+ try:
+ when = int(when)
+ except ValueError:
+ if when == 'before':
+ when = 0
+ elif when == 'after':
+ when = -1
+ else:
+ err('invalid \"when\" option value \"%s\" in %s' % (when, name))
+ return Rule(name, event, io, when)
+
+def parse_config(config):
+ rules = []
+ for name in config.sections():
+ if name.startswith('inject-error'):
+ options = dict(config.items(name))
+ rules.append(parse_inject_error(name, options))
+ else:
+ err('invalid config section name: %s' % name)
+ return rules
+
+def load_rules(filename):
+ config = ConfigParser.RawConfigParser()
+ with open(filename, 'rt') as f:
+ config.readfp(f, filename)
+ return parse_config(config)
+
+def open_socket(path):
+ '''Open a TCP or UNIX domain listen socket'''
+ if ':' in path:
+ host, port = path.split(':', 1)
+ sock = socket.socket()
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ sock.bind((host, int(port)))
+ else:
+ sock = socket.socket(socket.AF_UNIX)
+ sock.bind(path)
+ sock.listen(0)
+ print 'Listening on %s' % path
+ return sock
+
+def usage(args):
+ sys.stderr.write('usage: %s [--classic-negotiation] <tcp-port>|<unix-path> <config-file>\n' % args[0])
+ sys.stderr.write('Run an fault injector NBD server with rules defined in a config file.\n')
+ sys.exit(1)
+
+def main(args):
+ if len(args) != 3 and len(args) != 4:
+ usage(args)
+ use_export = True
+ if args[1] == '--classic-negotiation':
+ use_export = False
+ elif len(args) == 4:
+ usage(args)
+ sock = open_socket(args[1 if use_export else 2])
+ rules = load_rules(args[2 if use_export else 3])
+ run_server(sock, rules, use_export)
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
--
1.8.5.3
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [Qemu-devel] [PULL for-2.0 5/6] qemu-iotests: add 083 NBD client disconnect tests
2014-03-14 16:14 [Qemu-devel] [PULL for-2.0 0/6] Block patches Stefan Hajnoczi
` (3 preceding siblings ...)
2014-03-14 16:14 ` [Qemu-devel] [PULL for-2.0 4/6] tests: add nbd-fault-injector.py utility Stefan Hajnoczi
@ 2014-03-14 16:14 ` Stefan Hajnoczi
2014-03-14 16:14 ` [Qemu-devel] [PULL for-2.0 6/6] qemu-iotests: remove 085 and 087 from 'quick' group Stefan Hajnoczi
2014-03-15 18:05 ` [Qemu-devel] [PULL for-2.0 0/6] Block patches Peter Maydell
6 siblings, 0 replies; 8+ messages in thread
From: Stefan Hajnoczi @ 2014-03-14 16:14 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Maydell, Stefan Hajnoczi, Anthony Liguori
This new test case uses nbd-fault-injector.py to simulate broken TCP
connections at each stage in the NBD protocol. This way we can exercise
block/nbd-client.c's socket error handling code paths.
In particular, this serves as a regression test to make sure
nbd-client.c doesn't cause an infinite loop by leaving its
nbd_receive_reply() fd handler registered after the connection has been
closed. This bug was fixed in an earlier patch.
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
tests/qemu-iotests/083 | 129 +++++++++++++++++++++++++++++++++++
tests/qemu-iotests/083.out | 163 +++++++++++++++++++++++++++++++++++++++++++++
tests/qemu-iotests/group | 1 +
3 files changed, 293 insertions(+)
create mode 100755 tests/qemu-iotests/083
create mode 100644 tests/qemu-iotests/083.out
diff --git a/tests/qemu-iotests/083 b/tests/qemu-iotests/083
new file mode 100755
index 0000000..f764534
--- /dev/null
+++ b/tests/qemu-iotests/083
@@ -0,0 +1,129 @@
+#!/bin/bash
+#
+# Test NBD client unexpected disconnect
+#
+# Copyright Red Hat, Inc. 2014
+#
+# 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=stefanha@redhat.com
+
+seq=`basename $0`
+echo "QA output created by $seq"
+
+here=`pwd`
+tmp=/tmp/$$
+status=1 # failure is the default!
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+
+_supported_fmt generic
+_supported_proto nbd
+_supported_os Linux
+
+# Pick a TCP port based on our pid. This way multiple instances of this test
+# can run in parallel without conflicting.
+choose_tcp_port() {
+ echo $((($$ % 31744) + 1024)) # 1024 <= port < 32768
+}
+
+wait_for_tcp_port() {
+ while ! (netstat --tcp --listening --numeric | \
+ grep "$1.*0.0.0.0:\*.*LISTEN") 2>&1 >/dev/null; do
+ sleep 0.1
+ done
+}
+
+filter_nbd() {
+ # nbd.c error messages contain function names and line numbers that are prone
+ # to change. Message ordering depends on timing between send and receive
+ # callbacks sometimes, making them unreliable.
+ #
+ # Filter out the TCP port number since this changes between runs.
+ sed -e 's#^nbd.c:.*##g' \
+ -e 's#nbd:127.0.0.1:[^:]*:#nbd:127.0.0.1:PORT:#g'
+}
+
+check_disconnect() {
+ event=$1
+ when=$2
+ negotiation=$3
+ echo "=== Check disconnect $when $event ==="
+ echo
+
+ port=$(choose_tcp_port)
+
+ cat > "$TEST_DIR/nbd-fault-injector.conf" <<EOF
+[inject-error]
+event=$event
+when=$when
+EOF
+
+ if [ "$negotiation" = "--classic-negotiation" ]; then
+ extra_args=--classic-negotiation
+ nbd_url="nbd:127.0.0.1:$port"
+ else
+ nbd_url="nbd:127.0.0.1:$port:exportname=foo"
+ fi
+
+ ./nbd-fault-injector.py $extra_args "127.0.0.1:$port" "$TEST_DIR/nbd-fault-injector.conf" 2>&1 >/dev/null &
+ wait_for_tcp_port "127.0.0.1:$port"
+ $QEMU_IO -c "read 0 512" "$nbd_url" 2>&1 | _filter_qemu_io | filter_nbd
+
+ echo
+}
+
+for event in neg1 "export" neg2 request reply data; do
+ for when in before after; do
+ check_disconnect "$event" "$when"
+ done
+
+ # Also inject short replies from the NBD server
+ case "$event" in
+ neg1)
+ for when in 8 16; do
+ check_disconnect "$event" "$when"
+ done
+ ;;
+ "export")
+ for when in 4 12 16; do
+ check_disconnect "$event" "$when"
+ done
+ ;;
+ neg2)
+ for when in 8 10; do
+ check_disconnect "$event" "$when"
+ done
+ ;;
+ reply)
+ for when in 4 8; do
+ check_disconnect "$event" "$when"
+ done
+ ;;
+ esac
+done
+
+# Also check classic negotiation without export information
+for when in before 8 16 24 28 after; do
+ check_disconnect "neg-classic" "$when" --classic-negotiation
+done
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/083.out b/tests/qemu-iotests/083.out
new file mode 100644
index 0000000..85ee8d6
--- /dev/null
+++ b/tests/qemu-iotests/083.out
@@ -0,0 +1,163 @@
+QA output created by 083
+=== Check disconnect before neg1 ===
+
+
+qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo: Could not open image: Invalid argument
+no file open, try 'help open'
+
+=== Check disconnect after neg1 ===
+
+
+qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo: Could not open image: Invalid argument
+no file open, try 'help open'
+
+=== Check disconnect 8 neg1 ===
+
+
+qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo: Could not open image: Invalid argument
+no file open, try 'help open'
+
+=== Check disconnect 16 neg1 ===
+
+
+qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo: Could not open image: Invalid argument
+no file open, try 'help open'
+
+=== Check disconnect before export ===
+
+
+qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo: Could not open image: Invalid argument
+no file open, try 'help open'
+
+=== Check disconnect after export ===
+
+
+qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo: Could not open image: Invalid argument
+no file open, try 'help open'
+
+=== Check disconnect 4 export ===
+
+
+qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo: Could not open image: Invalid argument
+no file open, try 'help open'
+
+=== Check disconnect 12 export ===
+
+
+qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo: Could not open image: Invalid argument
+no file open, try 'help open'
+
+=== Check disconnect 16 export ===
+
+
+qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo: Could not open image: Invalid argument
+no file open, try 'help open'
+
+=== Check disconnect before neg2 ===
+
+
+qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo: Could not open image: Invalid argument
+no file open, try 'help open'
+
+=== Check disconnect after neg2 ===
+
+
+qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo: Could not read image for determining its format: Input/output error
+no file open, try 'help open'
+
+=== Check disconnect 8 neg2 ===
+
+
+qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo: Could not open image: Invalid argument
+no file open, try 'help open'
+
+=== Check disconnect 10 neg2 ===
+
+
+qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo: Could not open image: Invalid argument
+no file open, try 'help open'
+
+=== Check disconnect before request ===
+
+
+qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo: Could not read image for determining its format: Input/output error
+no file open, try 'help open'
+
+=== Check disconnect after request ===
+
+
+qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo: Could not read image for determining its format: Input/output error
+no file open, try 'help open'
+
+=== Check disconnect before reply ===
+
+
+qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo: Could not read image for determining its format: Input/output error
+no file open, try 'help open'
+
+=== Check disconnect after reply ===
+
+
+qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo: Could not read image for determining its format: Input/output error
+no file open, try 'help open'
+
+=== Check disconnect 4 reply ===
+
+
+qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo: Could not read image for determining its format: Input/output error
+no file open, try 'help open'
+
+=== Check disconnect 8 reply ===
+
+
+qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo: Could not read image for determining its format: Input/output error
+no file open, try 'help open'
+
+=== Check disconnect before data ===
+
+
+qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo: Could not read image for determining its format: Input/output error
+no file open, try 'help open'
+
+=== Check disconnect after data ===
+
+
+read failed: Input/output error
+
+=== Check disconnect before neg-classic ===
+
+
+qemu-io: can't open device nbd:127.0.0.1:PORT: Could not open image: Invalid argument
+no file open, try 'help open'
+
+=== Check disconnect 8 neg-classic ===
+
+
+qemu-io: can't open device nbd:127.0.0.1:PORT: Could not open image: Invalid argument
+no file open, try 'help open'
+
+=== Check disconnect 16 neg-classic ===
+
+
+qemu-io: can't open device nbd:127.0.0.1:PORT: Could not open image: Invalid argument
+no file open, try 'help open'
+
+=== Check disconnect 24 neg-classic ===
+
+
+qemu-io: can't open device nbd:127.0.0.1:PORT: Could not open image: Invalid argument
+no file open, try 'help open'
+
+=== Check disconnect 28 neg-classic ===
+
+
+qemu-io: can't open device nbd:127.0.0.1:PORT: Could not open image: Invalid argument
+no file open, try 'help open'
+
+=== Check disconnect after neg-classic ===
+
+
+qemu-io: can't open device nbd:127.0.0.1:PORT: Could not read image for determining its format: Input/output error
+no file open, try 'help open'
+
+*** done
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
index e96eafd..40cd3b3 100644
--- a/tests/qemu-iotests/group
+++ b/tests/qemu-iotests/group
@@ -85,6 +85,7 @@
079 rw auto
081 rw auto
082 rw auto quick
+083 rw auto
085 rw auto quick
086 rw auto quick
087 rw auto quick
--
1.8.5.3
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [Qemu-devel] [PULL for-2.0 6/6] qemu-iotests: remove 085 and 087 from 'quick' group
2014-03-14 16:14 [Qemu-devel] [PULL for-2.0 0/6] Block patches Stefan Hajnoczi
` (4 preceding siblings ...)
2014-03-14 16:14 ` [Qemu-devel] [PULL for-2.0 5/6] qemu-iotests: add 083 NBD client disconnect tests Stefan Hajnoczi
@ 2014-03-14 16:14 ` Stefan Hajnoczi
2014-03-15 18:05 ` [Qemu-devel] [PULL for-2.0 0/6] Block patches Peter Maydell
6 siblings, 0 replies; 8+ messages in thread
From: Stefan Hajnoczi @ 2014-03-14 16:14 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Maydell, Stefan Hajnoczi, Anthony Liguori
The 'quick' group in qemu-iotests are not allowed to run QEMU since we
don't know which targets are available. In other words, they may only
use qemu-img, qemu-io, and qemu-nbd.
Drop 085 and 087 from the 'quick' group since they run QEMU. This
makes "make check-block" pass again.
Reported-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
tests/qemu-iotests/group | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
index 40cd3b3..ee09ebc 100644
--- a/tests/qemu-iotests/group
+++ b/tests/qemu-iotests/group
@@ -86,6 +86,6 @@
081 rw auto
082 rw auto quick
083 rw auto
-085 rw auto quick
+085 rw auto
086 rw auto quick
-087 rw auto quick
+087 rw auto
--
1.8.5.3
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [Qemu-devel] [PULL for-2.0 0/6] Block patches
2014-03-14 16:14 [Qemu-devel] [PULL for-2.0 0/6] Block patches Stefan Hajnoczi
` (5 preceding siblings ...)
2014-03-14 16:14 ` [Qemu-devel] [PULL for-2.0 6/6] qemu-iotests: remove 085 and 087 from 'quick' group Stefan Hajnoczi
@ 2014-03-15 18:05 ` Peter Maydell
6 siblings, 0 replies; 8+ messages in thread
From: Peter Maydell @ 2014-03-15 18:05 UTC (permalink / raw)
To: Stefan Hajnoczi; +Cc: QEMU Developers, Anthony Liguori
On 14 March 2014 16:14, Stefan Hajnoczi <stefanha@redhat.com> wrote:
> Bug fixes for the next QEMU 2.0 release candidate.
>
> The following changes since commit 5d92c74f8a1728a202ba9457872ab0f27ff15e81:
>
> Update version for v2.0-rc0 (2014-03-13 20:08:15 -0700)
>
> are available in the git repository at:
>
> git@github.com:stefanha/qemu.git tags/block-pull-request
>
> for you to fetch changes up to 46dea4160d587add2f3670306c41ad9ad4064af5:
>
> qemu-iotests: remove 085 and 087 from 'quick' group (2014-03-14 16:30:38 +0100)
>
> ----------------------------------------------------------------
> Block pull request
Applied, thanks.
-- PMM
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2014-03-15 18:05 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-03-14 16:14 [Qemu-devel] [PULL for-2.0 0/6] Block patches Stefan Hajnoczi
2014-03-14 16:14 ` [Qemu-devel] [PULL for-2.0 1/6] blockdev: Refuse to open encrypted image unless paused Stefan Hajnoczi
2014-03-14 16:14 ` [Qemu-devel] [PULL for-2.0 2/6] block: Explicitly specify 'unsigned long long' for VHDX 64-bit constants Stefan Hajnoczi
2014-03-14 16:14 ` [Qemu-devel] [PULL for-2.0 3/6] nbd: close socket if connection breaks Stefan Hajnoczi
2014-03-14 16:14 ` [Qemu-devel] [PULL for-2.0 4/6] tests: add nbd-fault-injector.py utility Stefan Hajnoczi
2014-03-14 16:14 ` [Qemu-devel] [PULL for-2.0 5/6] qemu-iotests: add 083 NBD client disconnect tests Stefan Hajnoczi
2014-03-14 16:14 ` [Qemu-devel] [PULL for-2.0 6/6] qemu-iotests: remove 085 and 087 from 'quick' group Stefan Hajnoczi
2014-03-15 18:05 ` [Qemu-devel] [PULL for-2.0 0/6] Block patches Peter Maydell
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).