nbd patches for 2018-01-26
- Vladimir Sementsov-Ogievskiy - nbd export qmp interface - Eric Blake - hmp: Add nbd_server_remove to mirror QMP command - Edgar Kaziakhmedov - nbd: implement bdrv_get_info callback -----BEGIN PGP SIGNATURE----- Comment: Public key at http://people.redhat.com/eblake/eblake.gpg iQEcBAABCAAGBQJaa1EaAAoJEKeha0olJ0NqmBUH+gPukp0LJUDd8GKgoTVLumys lRJ6+XPosN/z9mKuM4KFUDRnOV25N5Jq+iyYUHdOzL2WVaM1JWJXo45qPgNGtRPc feQ2oF3YfSZ+OvpyemDoV1CfQJdjc+/SfDeOrwR6diaxIvW9nezDlSDQxNnMVP4I yexNEFa92+e6lmjXxb+TVzwp2HGb1GQu+yV3pB2sVeAdU3ldWMqTMKjzGMDtvgrM C7WdVXGaE2geqMZKmbYtSGwmRcEgC62zn9luCnCD30SSfy4VkpdCcsBLWfyd0UC3 ZIS2dDWuoyGAk1hZlNxzUxdWLrg79PstL4w5FlCqYGlklm31qvrqZj/4AcCeny8= =yaSE -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/ericb/tags/pull-nbd-2018-01-26' into staging nbd patches for 2018-01-26 - Vladimir Sementsov-Ogievskiy - nbd export qmp interface - Eric Blake - hmp: Add nbd_server_remove to mirror QMP command - Edgar Kaziakhmedov - nbd: implement bdrv_get_info callback # gpg: Signature made Fri 26 Jan 2018 16:02:34 GMT # gpg: using RSA key 0xA7A16B4A2527436A # gpg: Good signature from "Eric Blake <eblake@redhat.com>" # gpg: aka "Eric Blake (Free Software Programmer) <ebb9@byu.net>" # gpg: aka "[jpeg image of size 6874]" # Primary key fingerprint: 71C2 CC22 B1C4 6029 27D2 F3AA A7A1 6B4A 2527 436A * remotes/ericb/tags/pull-nbd-2018-01-26: nbd: implement bdrv_get_info callback hmp: Add nbd_server_remove to mirror QMP command iotest 205: new test for qmp nbd-server-remove iotests: implement QemuIoInteractive class iotest 147: add cases to test new @name parameter of nbd-server-add qapi: add nbd-server-remove hmp: Add name parameter to nbd_server_add qapi: add name parameter to nbd-server-add Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
6233b4a8c2
11
block/nbd.c
11
block/nbd.c
@ -566,6 +566,14 @@ static void nbd_refresh_filename(BlockDriverState *bs, QDict *options)
|
|||||||
bs->full_open_options = opts;
|
bs->full_open_options = opts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int nbd_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
||||||
|
{
|
||||||
|
if (bs->supported_zero_flags & BDRV_REQ_MAY_UNMAP) {
|
||||||
|
bdi->can_write_zeroes_with_unmap = true;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static BlockDriver bdrv_nbd = {
|
static BlockDriver bdrv_nbd = {
|
||||||
.format_name = "nbd",
|
.format_name = "nbd",
|
||||||
.protocol_name = "nbd",
|
.protocol_name = "nbd",
|
||||||
@ -583,6 +591,7 @@ static BlockDriver bdrv_nbd = {
|
|||||||
.bdrv_detach_aio_context = nbd_detach_aio_context,
|
.bdrv_detach_aio_context = nbd_detach_aio_context,
|
||||||
.bdrv_attach_aio_context = nbd_attach_aio_context,
|
.bdrv_attach_aio_context = nbd_attach_aio_context,
|
||||||
.bdrv_refresh_filename = nbd_refresh_filename,
|
.bdrv_refresh_filename = nbd_refresh_filename,
|
||||||
|
.bdrv_get_info = nbd_get_info,
|
||||||
};
|
};
|
||||||
|
|
||||||
static BlockDriver bdrv_nbd_tcp = {
|
static BlockDriver bdrv_nbd_tcp = {
|
||||||
@ -602,6 +611,7 @@ static BlockDriver bdrv_nbd_tcp = {
|
|||||||
.bdrv_detach_aio_context = nbd_detach_aio_context,
|
.bdrv_detach_aio_context = nbd_detach_aio_context,
|
||||||
.bdrv_attach_aio_context = nbd_attach_aio_context,
|
.bdrv_attach_aio_context = nbd_attach_aio_context,
|
||||||
.bdrv_refresh_filename = nbd_refresh_filename,
|
.bdrv_refresh_filename = nbd_refresh_filename,
|
||||||
|
.bdrv_get_info = nbd_get_info,
|
||||||
};
|
};
|
||||||
|
|
||||||
static BlockDriver bdrv_nbd_unix = {
|
static BlockDriver bdrv_nbd_unix = {
|
||||||
@ -621,6 +631,7 @@ static BlockDriver bdrv_nbd_unix = {
|
|||||||
.bdrv_detach_aio_context = nbd_detach_aio_context,
|
.bdrv_detach_aio_context = nbd_detach_aio_context,
|
||||||
.bdrv_attach_aio_context = nbd_attach_aio_context,
|
.bdrv_attach_aio_context = nbd_attach_aio_context,
|
||||||
.bdrv_refresh_filename = nbd_refresh_filename,
|
.bdrv_refresh_filename = nbd_refresh_filename,
|
||||||
|
.bdrv_get_info = nbd_get_info,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void bdrv_nbd_init(void)
|
static void bdrv_nbd_init(void)
|
||||||
|
@ -140,8 +140,8 @@ void qmp_nbd_server_start(SocketAddressLegacy *addr,
|
|||||||
qapi_free_SocketAddress(addr_flat);
|
qapi_free_SocketAddress(addr_flat);
|
||||||
}
|
}
|
||||||
|
|
||||||
void qmp_nbd_server_add(const char *device, bool has_writable, bool writable,
|
void qmp_nbd_server_add(const char *device, bool has_name, const char *name,
|
||||||
Error **errp)
|
bool has_writable, bool writable, Error **errp)
|
||||||
{
|
{
|
||||||
BlockDriverState *bs = NULL;
|
BlockDriverState *bs = NULL;
|
||||||
BlockBackend *on_eject_blk;
|
BlockBackend *on_eject_blk;
|
||||||
@ -152,8 +152,12 @@ void qmp_nbd_server_add(const char *device, bool has_writable, bool writable,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nbd_export_find(device)) {
|
if (!has_name) {
|
||||||
error_setg(errp, "NBD server already exporting device '%s'", device);
|
name = device;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nbd_export_find(name)) {
|
||||||
|
error_setg(errp, "NBD server already has export named '%s'", name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,7 +181,7 @@ void qmp_nbd_server_add(const char *device, bool has_writable, bool writable,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
nbd_export_set_name(exp, device);
|
nbd_export_set_name(exp, name);
|
||||||
|
|
||||||
/* The list of named exports has a strong reference to this export now and
|
/* The list of named exports has a strong reference to this export now and
|
||||||
* our only way of accessing it is through nbd_export_find(), so we can drop
|
* our only way of accessing it is through nbd_export_find(), so we can drop
|
||||||
@ -185,6 +189,30 @@ void qmp_nbd_server_add(const char *device, bool has_writable, bool writable,
|
|||||||
nbd_export_put(exp);
|
nbd_export_put(exp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void qmp_nbd_server_remove(const char *name,
|
||||||
|
bool has_mode, NbdServerRemoveMode mode,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
NBDExport *exp;
|
||||||
|
|
||||||
|
if (!nbd_server) {
|
||||||
|
error_setg(errp, "NBD server not running");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
exp = nbd_export_find(name);
|
||||||
|
if (exp == NULL) {
|
||||||
|
error_setg(errp, "Export '%s' is not found", name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!has_mode) {
|
||||||
|
mode = NBD_SERVER_REMOVE_MODE_SAFE;
|
||||||
|
}
|
||||||
|
|
||||||
|
nbd_export_remove(exp, mode, errp);
|
||||||
|
}
|
||||||
|
|
||||||
void qmp_nbd_server_stop(Error **errp)
|
void qmp_nbd_server_stop(Error **errp)
|
||||||
{
|
{
|
||||||
nbd_export_close_all();
|
nbd_export_close_all();
|
||||||
|
@ -1553,17 +1553,35 @@ ETEXI
|
|||||||
|
|
||||||
{
|
{
|
||||||
.name = "nbd_server_add",
|
.name = "nbd_server_add",
|
||||||
.args_type = "writable:-w,device:B",
|
.args_type = "writable:-w,device:B,name:s?",
|
||||||
.params = "nbd_server_add [-w] device",
|
.params = "nbd_server_add [-w] device [name]",
|
||||||
.help = "export a block device via NBD",
|
.help = "export a block device via NBD",
|
||||||
.cmd = hmp_nbd_server_add,
|
.cmd = hmp_nbd_server_add,
|
||||||
},
|
},
|
||||||
STEXI
|
STEXI
|
||||||
@item nbd_server_add @var{device}
|
@item nbd_server_add @var{device} [ @var{name} ]
|
||||||
@findex nbd_server_add
|
@findex nbd_server_add
|
||||||
Export a block device through QEMU's NBD server, which must be started
|
Export a block device through QEMU's NBD server, which must be started
|
||||||
beforehand with @command{nbd_server_start}. The @option{-w} option makes the
|
beforehand with @command{nbd_server_start}. The @option{-w} option makes the
|
||||||
exported device writable too.
|
exported device writable too. The export name is controlled by @var{name},
|
||||||
|
defaulting to @var{device}.
|
||||||
|
ETEXI
|
||||||
|
|
||||||
|
{
|
||||||
|
.name = "nbd_server_remove",
|
||||||
|
.args_type = "force:-f,name:s",
|
||||||
|
.params = "nbd_server_remove [-f] name",
|
||||||
|
.help = "remove an export previously exposed via NBD",
|
||||||
|
.cmd = hmp_nbd_server_remove,
|
||||||
|
},
|
||||||
|
STEXI
|
||||||
|
@item nbd_server_remove [-f] @var{name}
|
||||||
|
@findex nbd_server_remove
|
||||||
|
Stop exporting a block device through QEMU's NBD server, which was
|
||||||
|
previously started with @command{nbd_server_add}. The @option{-f}
|
||||||
|
option forces the server to drop the export immediately even if
|
||||||
|
clients are connected; otherwise the command fails unless there are no
|
||||||
|
clients.
|
||||||
ETEXI
|
ETEXI
|
||||||
|
|
||||||
{
|
{
|
||||||
|
20
hmp.c
20
hmp.c
@ -2203,7 +2203,8 @@ void hmp_nbd_server_start(Monitor *mon, const QDict *qdict)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
qmp_nbd_server_add(info->value->device, true, writable, &local_err);
|
qmp_nbd_server_add(info->value->device, false, NULL,
|
||||||
|
true, writable, &local_err);
|
||||||
|
|
||||||
if (local_err != NULL) {
|
if (local_err != NULL) {
|
||||||
qmp_nbd_server_stop(NULL);
|
qmp_nbd_server_stop(NULL);
|
||||||
@ -2220,14 +2221,23 @@ exit:
|
|||||||
void hmp_nbd_server_add(Monitor *mon, const QDict *qdict)
|
void hmp_nbd_server_add(Monitor *mon, const QDict *qdict)
|
||||||
{
|
{
|
||||||
const char *device = qdict_get_str(qdict, "device");
|
const char *device = qdict_get_str(qdict, "device");
|
||||||
|
const char *name = qdict_get_try_str(qdict, "name");
|
||||||
bool writable = qdict_get_try_bool(qdict, "writable", false);
|
bool writable = qdict_get_try_bool(qdict, "writable", false);
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
|
|
||||||
qmp_nbd_server_add(device, true, writable, &local_err);
|
qmp_nbd_server_add(device, !!name, name, true, writable, &local_err);
|
||||||
|
hmp_handle_error(mon, &local_err);
|
||||||
|
}
|
||||||
|
|
||||||
if (local_err != NULL) {
|
void hmp_nbd_server_remove(Monitor *mon, const QDict *qdict)
|
||||||
hmp_handle_error(mon, &local_err);
|
{
|
||||||
}
|
const char *name = qdict_get_str(qdict, "name");
|
||||||
|
bool force = qdict_get_try_bool(qdict, "force", false);
|
||||||
|
Error *err = NULL;
|
||||||
|
|
||||||
|
/* Rely on NBD_SERVER_REMOVE_MODE_SAFE being the default */
|
||||||
|
qmp_nbd_server_remove(name, force, NBD_SERVER_REMOVE_MODE_HARD, &err);
|
||||||
|
hmp_handle_error(mon, &err);
|
||||||
}
|
}
|
||||||
|
|
||||||
void hmp_nbd_server_stop(Monitor *mon, const QDict *qdict)
|
void hmp_nbd_server_stop(Monitor *mon, const QDict *qdict)
|
||||||
|
1
hmp.h
1
hmp.h
@ -101,6 +101,7 @@ void hmp_sendkey(Monitor *mon, const QDict *qdict);
|
|||||||
void hmp_screendump(Monitor *mon, const QDict *qdict);
|
void hmp_screendump(Monitor *mon, const QDict *qdict);
|
||||||
void hmp_nbd_server_start(Monitor *mon, const QDict *qdict);
|
void hmp_nbd_server_start(Monitor *mon, const QDict *qdict);
|
||||||
void hmp_nbd_server_add(Monitor *mon, const QDict *qdict);
|
void hmp_nbd_server_add(Monitor *mon, const QDict *qdict);
|
||||||
|
void hmp_nbd_server_remove(Monitor *mon, const QDict *qdict);
|
||||||
void hmp_nbd_server_stop(Monitor *mon, const QDict *qdict);
|
void hmp_nbd_server_stop(Monitor *mon, const QDict *qdict);
|
||||||
void hmp_chardev_add(Monitor *mon, const QDict *qdict);
|
void hmp_chardev_add(Monitor *mon, const QDict *qdict);
|
||||||
void hmp_chardev_change(Monitor *mon, const QDict *qdict);
|
void hmp_chardev_change(Monitor *mon, const QDict *qdict);
|
||||||
|
@ -261,6 +261,7 @@ NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size,
|
|||||||
bool writethrough, BlockBackend *on_eject_blk,
|
bool writethrough, BlockBackend *on_eject_blk,
|
||||||
Error **errp);
|
Error **errp);
|
||||||
void nbd_export_close(NBDExport *exp);
|
void nbd_export_close(NBDExport *exp);
|
||||||
|
void nbd_export_remove(NBDExport *exp, NbdServerRemoveMode mode, Error **errp);
|
||||||
void nbd_export_get(NBDExport *exp);
|
void nbd_export_get(NBDExport *exp);
|
||||||
void nbd_export_put(NBDExport *exp);
|
void nbd_export_put(NBDExport *exp);
|
||||||
|
|
||||||
|
13
nbd/server.c
13
nbd/server.c
@ -1177,6 +1177,19 @@ void nbd_export_close(NBDExport *exp)
|
|||||||
nbd_export_put(exp);
|
nbd_export_put(exp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void nbd_export_remove(NBDExport *exp, NbdServerRemoveMode mode, Error **errp)
|
||||||
|
{
|
||||||
|
if (mode == NBD_SERVER_REMOVE_MODE_HARD || QTAILQ_EMPTY(&exp->clients)) {
|
||||||
|
nbd_export_close(exp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(mode == NBD_SERVER_REMOVE_MODE_SAFE);
|
||||||
|
|
||||||
|
error_setg(errp, "export '%s' still in use", exp->name);
|
||||||
|
error_append_hint(errp, "Use mode='hard' to force client disconnect\n");
|
||||||
|
}
|
||||||
|
|
||||||
void nbd_export_get(NBDExport *exp)
|
void nbd_export_get(NBDExport *exp)
|
||||||
{
|
{
|
||||||
assert(exp->refcount > 0);
|
assert(exp->refcount > 0);
|
||||||
|
@ -213,14 +213,60 @@
|
|||||||
#
|
#
|
||||||
# @device: The device name or node name of the node to be exported
|
# @device: The device name or node name of the node to be exported
|
||||||
#
|
#
|
||||||
|
# @name: Export name. If unspecified, the @device parameter is used as the
|
||||||
|
# export name. (Since 2.12)
|
||||||
|
#
|
||||||
# @writable: Whether clients should be able to write to the device via the
|
# @writable: Whether clients should be able to write to the device via the
|
||||||
# NBD connection (default false).
|
# NBD connection (default false).
|
||||||
#
|
#
|
||||||
# Returns: error if the device is already marked for export.
|
# Returns: error if the server is not running, or export with the same name
|
||||||
|
# already exists.
|
||||||
#
|
#
|
||||||
# Since: 1.3.0
|
# Since: 1.3.0
|
||||||
##
|
##
|
||||||
{ 'command': 'nbd-server-add', 'data': {'device': 'str', '*writable': 'bool'} }
|
{ 'command': 'nbd-server-add',
|
||||||
|
'data': {'device': 'str', '*name': 'str', '*writable': 'bool'} }
|
||||||
|
|
||||||
|
##
|
||||||
|
# @NbdServerRemoveMode:
|
||||||
|
#
|
||||||
|
# Mode for removing an NBD export.
|
||||||
|
#
|
||||||
|
# @safe: Remove export if there are no existing connections, fail otherwise.
|
||||||
|
#
|
||||||
|
# @hard: Drop all connections immediately and remove export.
|
||||||
|
#
|
||||||
|
# Potential additional modes to be added in the future:
|
||||||
|
#
|
||||||
|
# hide: Just hide export from new clients, leave existing connections as is.
|
||||||
|
# Remove export after all clients are disconnected.
|
||||||
|
#
|
||||||
|
# soft: Hide export from new clients, answer with ESHUTDOWN for all further
|
||||||
|
# requests from existing clients.
|
||||||
|
#
|
||||||
|
# Since: 2.12
|
||||||
|
##
|
||||||
|
{'enum': 'NbdServerRemoveMode', 'data': ['safe', 'hard']}
|
||||||
|
|
||||||
|
##
|
||||||
|
# @nbd-server-remove:
|
||||||
|
#
|
||||||
|
# Remove NBD export by name.
|
||||||
|
#
|
||||||
|
# @name: Export name.
|
||||||
|
#
|
||||||
|
# @mode: Mode of command operation. See @NbdServerRemoveMode description.
|
||||||
|
# Default is 'safe'.
|
||||||
|
#
|
||||||
|
# Returns: error if
|
||||||
|
# - the server is not running
|
||||||
|
# - export is not found
|
||||||
|
# - mode is 'safe' and there are existing connections
|
||||||
|
#
|
||||||
|
# Since: 2.12
|
||||||
|
##
|
||||||
|
{ 'command': 'nbd-server-remove',
|
||||||
|
'data': {'name': 'str', '*mode': 'NbdServerRemoveMode'} }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @nbd-server-stop:
|
# @nbd-server-stop:
|
||||||
|
@ -38,8 +38,8 @@ def flatten_sock_addr(crumpled_address):
|
|||||||
|
|
||||||
|
|
||||||
class NBDBlockdevAddBase(iotests.QMPTestCase):
|
class NBDBlockdevAddBase(iotests.QMPTestCase):
|
||||||
def blockdev_add_options(self, address, export=None):
|
def blockdev_add_options(self, address, export, node_name):
|
||||||
options = { 'node-name': 'nbd-blockdev',
|
options = { 'node-name': node_name,
|
||||||
'driver': 'raw',
|
'driver': 'raw',
|
||||||
'file': {
|
'file': {
|
||||||
'driver': 'nbd',
|
'driver': 'nbd',
|
||||||
@ -50,23 +50,28 @@ class NBDBlockdevAddBase(iotests.QMPTestCase):
|
|||||||
options['file']['export'] = export
|
options['file']['export'] = export
|
||||||
return options
|
return options
|
||||||
|
|
||||||
def client_test(self, filename, address, export=None):
|
def client_test(self, filename, address, export=None,
|
||||||
bao = self.blockdev_add_options(address, export)
|
node_name='nbd-blockdev', delete=True):
|
||||||
|
bao = self.blockdev_add_options(address, export, node_name)
|
||||||
result = self.vm.qmp('blockdev-add', **bao)
|
result = self.vm.qmp('blockdev-add', **bao)
|
||||||
self.assert_qmp(result, 'return', {})
|
self.assert_qmp(result, 'return', {})
|
||||||
|
|
||||||
|
found = False
|
||||||
result = self.vm.qmp('query-named-block-nodes')
|
result = self.vm.qmp('query-named-block-nodes')
|
||||||
for node in result['return']:
|
for node in result['return']:
|
||||||
if node['node-name'] == 'nbd-blockdev':
|
if node['node-name'] == node_name:
|
||||||
|
found = True
|
||||||
if isinstance(filename, str):
|
if isinstance(filename, str):
|
||||||
self.assert_qmp(node, 'image/filename', filename)
|
self.assert_qmp(node, 'image/filename', filename)
|
||||||
else:
|
else:
|
||||||
self.assert_json_filename_equal(node['image']['filename'],
|
self.assert_json_filename_equal(node['image']['filename'],
|
||||||
filename)
|
filename)
|
||||||
break
|
break
|
||||||
|
self.assertTrue(found)
|
||||||
|
|
||||||
result = self.vm.qmp('blockdev-del', node_name='nbd-blockdev')
|
if delete:
|
||||||
self.assert_qmp(result, 'return', {})
|
result = self.vm.qmp('blockdev-del', node_name=node_name)
|
||||||
|
self.assert_qmp(result, 'return', {})
|
||||||
|
|
||||||
|
|
||||||
class QemuNBD(NBDBlockdevAddBase):
|
class QemuNBD(NBDBlockdevAddBase):
|
||||||
@ -125,26 +130,63 @@ class BuiltinNBD(NBDBlockdevAddBase):
|
|||||||
except OSError:
|
except OSError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def _server_up(self, address):
|
def _server_up(self, address, export_name=None, export_name2=None):
|
||||||
result = self.server.qmp('nbd-server-start', addr=address)
|
result = self.server.qmp('nbd-server-start', addr=address)
|
||||||
self.assert_qmp(result, 'return', {})
|
self.assert_qmp(result, 'return', {})
|
||||||
|
|
||||||
result = self.server.qmp('nbd-server-add', device='nbd-export')
|
if export_name is None:
|
||||||
|
result = self.server.qmp('nbd-server-add', device='nbd-export')
|
||||||
|
else:
|
||||||
|
result = self.server.qmp('nbd-server-add', device='nbd-export',
|
||||||
|
name=export_name)
|
||||||
self.assert_qmp(result, 'return', {})
|
self.assert_qmp(result, 'return', {})
|
||||||
|
|
||||||
|
if export_name2 is not None:
|
||||||
|
result = self.server.qmp('nbd-server-add', device='nbd-export',
|
||||||
|
name=export_name2)
|
||||||
|
self.assert_qmp(result, 'return', {})
|
||||||
|
|
||||||
|
|
||||||
def _server_down(self):
|
def _server_down(self):
|
||||||
result = self.server.qmp('nbd-server-stop')
|
result = self.server.qmp('nbd-server-stop')
|
||||||
self.assert_qmp(result, 'return', {})
|
self.assert_qmp(result, 'return', {})
|
||||||
|
|
||||||
def test_inet(self):
|
def do_test_inet(self, export_name=None):
|
||||||
address = { 'type': 'inet',
|
address = { 'type': 'inet',
|
||||||
'data': {
|
'data': {
|
||||||
'host': 'localhost',
|
'host': 'localhost',
|
||||||
'port': str(NBD_PORT)
|
'port': str(NBD_PORT)
|
||||||
} }
|
} }
|
||||||
self._server_up(address)
|
self._server_up(address, export_name)
|
||||||
self.client_test('nbd://localhost:%i/nbd-export' % NBD_PORT,
|
export_name = export_name or 'nbd-export'
|
||||||
flatten_sock_addr(address), 'nbd-export')
|
self.client_test('nbd://localhost:%i/%s' % (NBD_PORT, export_name),
|
||||||
|
flatten_sock_addr(address), export_name)
|
||||||
|
self._server_down()
|
||||||
|
|
||||||
|
def test_inet_default_export_name(self):
|
||||||
|
self.do_test_inet()
|
||||||
|
|
||||||
|
def test_inet_same_export_name(self):
|
||||||
|
self.do_test_inet('nbd-export')
|
||||||
|
|
||||||
|
def test_inet_different_export_name(self):
|
||||||
|
self.do_test_inet('shadow')
|
||||||
|
|
||||||
|
def test_inet_two_exports(self):
|
||||||
|
address = { 'type': 'inet',
|
||||||
|
'data': {
|
||||||
|
'host': 'localhost',
|
||||||
|
'port': str(NBD_PORT)
|
||||||
|
} }
|
||||||
|
self._server_up(address, 'exp1', 'exp2')
|
||||||
|
self.client_test('nbd://localhost:%i/%s' % (NBD_PORT, 'exp1'),
|
||||||
|
flatten_sock_addr(address), 'exp1', 'node1', False)
|
||||||
|
self.client_test('nbd://localhost:%i/%s' % (NBD_PORT, 'exp2'),
|
||||||
|
flatten_sock_addr(address), 'exp2', 'node2', False)
|
||||||
|
result = self.vm.qmp('blockdev-del', node_name='node1')
|
||||||
|
self.assert_qmp(result, 'return', {})
|
||||||
|
result = self.vm.qmp('blockdev-del', node_name='node2')
|
||||||
|
self.assert_qmp(result, 'return', {})
|
||||||
self._server_down()
|
self._server_down()
|
||||||
|
|
||||||
def test_inet6(self):
|
def test_inet6(self):
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
......
|
.........
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
Ran 6 tests
|
Ran 9 tests
|
||||||
|
|
||||||
OK
|
OK
|
||||||
|
156
tests/qemu-iotests/205
Normal file
156
tests/qemu-iotests/205
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
#
|
||||||
|
# Tests for qmp command nbd-server-remove.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2017 Virtuozzo International GmbH
|
||||||
|
#
|
||||||
|
# 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 sys
|
||||||
|
import iotests
|
||||||
|
import time
|
||||||
|
from iotests import qemu_img, qemu_io, filter_qemu_io, QemuIoInteractive
|
||||||
|
|
||||||
|
nbd_sock = 'nbd_sock'
|
||||||
|
nbd_uri = 'nbd+unix:///exp?socket=' + nbd_sock
|
||||||
|
disk = os.path.join(iotests.test_dir, 'disk')
|
||||||
|
|
||||||
|
|
||||||
|
class TestNbdServerRemove(iotests.QMPTestCase):
|
||||||
|
def setUp(self):
|
||||||
|
qemu_img('create', '-f', iotests.imgfmt, disk, '1M')
|
||||||
|
|
||||||
|
self.vm = iotests.VM().add_drive(disk)
|
||||||
|
self.vm.launch()
|
||||||
|
|
||||||
|
address = {
|
||||||
|
'type': 'unix',
|
||||||
|
'data': {
|
||||||
|
'path': nbd_sock
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result = self.vm.qmp('nbd-server-start', addr=address)
|
||||||
|
self.assert_qmp(result, 'return', {})
|
||||||
|
result = self.vm.qmp('nbd-server-add', device='drive0', name='exp')
|
||||||
|
self.assert_qmp(result, 'return', {})
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.vm.shutdown()
|
||||||
|
os.remove(nbd_sock)
|
||||||
|
os.remove(disk)
|
||||||
|
|
||||||
|
def remove_export(self, name, mode=None):
|
||||||
|
if mode is None:
|
||||||
|
return self.vm.qmp('nbd-server-remove', name=name)
|
||||||
|
else:
|
||||||
|
return self.vm.qmp('nbd-server-remove', name=name, mode=mode)
|
||||||
|
|
||||||
|
def assertExportNotFound(self, name):
|
||||||
|
result = self.vm.qmp('nbd-server-remove', name=name)
|
||||||
|
self.assert_qmp(result, 'error/desc', "Export 'exp' is not found")
|
||||||
|
|
||||||
|
def assertExistingClients(self, result):
|
||||||
|
self.assert_qmp(result, 'error/desc', "export 'exp' still in use")
|
||||||
|
|
||||||
|
def assertReadOk(self, qemu_io_output):
|
||||||
|
self.assertEqual(
|
||||||
|
filter_qemu_io(qemu_io_output).strip(),
|
||||||
|
'read 512/512 bytes at offset 0\n' +
|
||||||
|
'512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)')
|
||||||
|
|
||||||
|
def assertReadFailed(self, qemu_io_output):
|
||||||
|
self.assertEqual(filter_qemu_io(qemu_io_output).strip(),
|
||||||
|
'read failed: Input/output error')
|
||||||
|
|
||||||
|
def assertConnectFailed(self, qemu_io_output):
|
||||||
|
self.assertEqual(filter_qemu_io(qemu_io_output).strip(),
|
||||||
|
"can't open device " + nbd_uri +
|
||||||
|
": Requested export not available\n"
|
||||||
|
"server reported: export 'exp' not present")
|
||||||
|
|
||||||
|
def do_test_connect_after_remove(self, mode=None):
|
||||||
|
args = ('-r', '-f', 'raw', '-c', 'read 0 512', nbd_uri)
|
||||||
|
self.assertReadOk(qemu_io(*args))
|
||||||
|
|
||||||
|
result = self.remove_export('exp', mode)
|
||||||
|
self.assert_qmp(result, 'return', {})
|
||||||
|
|
||||||
|
self.assertExportNotFound('exp')
|
||||||
|
self.assertConnectFailed(qemu_io(*args))
|
||||||
|
|
||||||
|
def test_connect_after_remove_default(self):
|
||||||
|
self.do_test_connect_after_remove()
|
||||||
|
|
||||||
|
def test_connect_after_remove_safe(self):
|
||||||
|
self.do_test_connect_after_remove('safe')
|
||||||
|
|
||||||
|
def test_connect_after_remove_force(self):
|
||||||
|
self.do_test_connect_after_remove('hard')
|
||||||
|
|
||||||
|
def do_test_remove_during_connect_safe(self, mode=None):
|
||||||
|
qio = QemuIoInteractive('-r', '-f', 'raw', nbd_uri)
|
||||||
|
self.assertReadOk(qio.cmd('read 0 512'))
|
||||||
|
|
||||||
|
result = self.remove_export('exp', mode)
|
||||||
|
self.assertExistingClients(result)
|
||||||
|
|
||||||
|
self.assertReadOk(qio.cmd('read 0 512'))
|
||||||
|
|
||||||
|
qio.close()
|
||||||
|
|
||||||
|
result = self.remove_export('exp', mode)
|
||||||
|
self.assert_qmp(result, 'return', {})
|
||||||
|
|
||||||
|
self.assertExportNotFound('exp')
|
||||||
|
|
||||||
|
def test_remove_during_connect_default(self):
|
||||||
|
self.do_test_remove_during_connect_safe()
|
||||||
|
|
||||||
|
def test_remove_during_connect_safe(self):
|
||||||
|
self.do_test_remove_during_connect_safe('safe')
|
||||||
|
|
||||||
|
def test_remove_during_connect_hard(self):
|
||||||
|
qio = QemuIoInteractive('-r', '-f', 'raw', nbd_uri)
|
||||||
|
self.assertReadOk(qio.cmd('read 0 512'))
|
||||||
|
|
||||||
|
result = self.remove_export('exp', 'hard')
|
||||||
|
self.assert_qmp(result, 'return', {})
|
||||||
|
|
||||||
|
self.assertReadFailed(qio.cmd('read 0 512'))
|
||||||
|
self.assertExportNotFound('exp')
|
||||||
|
|
||||||
|
qio.close()
|
||||||
|
|
||||||
|
def test_remove_during_connect_safe_hard(self):
|
||||||
|
qio = QemuIoInteractive('-r', '-f', 'raw', nbd_uri)
|
||||||
|
self.assertReadOk(qio.cmd('read 0 512'))
|
||||||
|
|
||||||
|
result = self.remove_export('exp', 'safe')
|
||||||
|
self.assertExistingClients(result)
|
||||||
|
|
||||||
|
self.assertReadOk(qio.cmd('read 0 512'))
|
||||||
|
|
||||||
|
result = self.remove_export('exp', 'hard')
|
||||||
|
self.assert_qmp(result, 'return', {})
|
||||||
|
|
||||||
|
self.assertExportNotFound('exp')
|
||||||
|
self.assertReadFailed(qio.cmd('read 0 512'))
|
||||||
|
qio.close()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
iotests.main()
|
5
tests/qemu-iotests/205.out
Normal file
5
tests/qemu-iotests/205.out
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
.......
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Ran 7 tests
|
||||||
|
|
||||||
|
OK
|
@ -201,3 +201,4 @@
|
|||||||
202 rw auto quick
|
202 rw auto quick
|
||||||
203 rw auto
|
203 rw auto
|
||||||
204 rw auto quick
|
204 rw auto quick
|
||||||
|
205 rw auto quick
|
||||||
|
@ -93,6 +93,44 @@ def qemu_io(*args):
|
|||||||
sys.stderr.write('qemu-io received signal %i: %s\n' % (-exitcode, ' '.join(args)))
|
sys.stderr.write('qemu-io received signal %i: %s\n' % (-exitcode, ' '.join(args)))
|
||||||
return subp.communicate()[0]
|
return subp.communicate()[0]
|
||||||
|
|
||||||
|
|
||||||
|
class QemuIoInteractive:
|
||||||
|
def __init__(self, *args):
|
||||||
|
self.args = qemu_io_args + list(args)
|
||||||
|
self._p = subprocess.Popen(self.args, stdin=subprocess.PIPE,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.STDOUT)
|
||||||
|
assert self._p.stdout.read(9) == 'qemu-io> '
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
self._p.communicate('q\n')
|
||||||
|
|
||||||
|
def _read_output(self):
|
||||||
|
pattern = 'qemu-io> '
|
||||||
|
n = len(pattern)
|
||||||
|
pos = 0
|
||||||
|
s = []
|
||||||
|
while pos != n:
|
||||||
|
c = self._p.stdout.read(1)
|
||||||
|
# check unexpected EOF
|
||||||
|
assert c != ''
|
||||||
|
s.append(c)
|
||||||
|
if c == pattern[pos]:
|
||||||
|
pos += 1
|
||||||
|
else:
|
||||||
|
pos = 0
|
||||||
|
|
||||||
|
return ''.join(s[:-n])
|
||||||
|
|
||||||
|
def cmd(self, cmd):
|
||||||
|
# quit command is in close(), '\n' is added automatically
|
||||||
|
assert '\n' not in cmd
|
||||||
|
cmd = cmd.strip()
|
||||||
|
assert cmd != 'q' and cmd != 'quit'
|
||||||
|
self._p.stdin.write(cmd + '\n')
|
||||||
|
return self._read_output()
|
||||||
|
|
||||||
|
|
||||||
def qemu_nbd(*args):
|
def qemu_nbd(*args):
|
||||||
'''Run qemu-nbd in daemon mode and return the parent's exit code'''
|
'''Run qemu-nbd in daemon mode and return the parent's exit code'''
|
||||||
return subprocess.call(qemu_nbd_args + ['--fork'] + list(args))
|
return subprocess.call(qemu_nbd_args + ['--fork'] + list(args))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user