[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-block] [PULL 20/37] block: add a 'blockdev-snapshot' QMP command
From: |
Kevin Wolf |
Subject: |
[Qemu-block] [PULL 20/37] block: add a 'blockdev-snapshot' QMP command |
Date: |
Thu, 5 Nov 2015 19:17:48 +0100 |
From: Alberto Garcia <address@hidden>
One of the limitations of the 'blockdev-snapshot-sync' command is that
it does not allow passing BlockdevOptions to the newly created
snapshots, so they are always opened using the default values.
Extending the command to allow passing options is not a practical
solution because there is overlap between those options and some of
the existing parameters of the command.
This patch introduces a new 'blockdev-snapshot' command with a simpler
interface: it just takes two references to existing block devices that
will be used as the source and target for the snapshot.
Since the main difference between the two commands is that one of them
creates and opens the target image, while the other uses an already
opened one, the bulk of the implementation is shared.
Signed-off-by: Alberto Garcia <address@hidden>
Reviewed-by: Max Reitz <address@hidden>
Signed-off-by: Kevin Wolf <address@hidden>
---
blockdev.c | 165 ++++++++++++++++++++++++++++++++-------------------
qapi-schema.json | 2 +
qapi/block-core.json | 28 +++++++++
qmp-commands.hx | 38 ++++++++++++
4 files changed, 172 insertions(+), 61 deletions(-)
diff --git a/blockdev.c b/blockdev.c
index 2237aaf..3a5183d 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -1175,6 +1175,18 @@ void qmp_blockdev_snapshot_sync(bool has_device, const
char *device,
&snapshot, errp);
}
+void qmp_blockdev_snapshot(const char *node, const char *overlay,
+ Error **errp)
+{
+ BlockdevSnapshot snapshot_data = {
+ .node = (char *) node,
+ .overlay = (char *) overlay
+ };
+
+ blockdev_do_action(TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT,
+ &snapshot_data, errp);
+}
+
void qmp_blockdev_snapshot_internal_sync(const char *device,
const char *name,
Error **errp)
@@ -1520,58 +1532,48 @@ typedef struct ExternalSnapshotState {
static void external_snapshot_prepare(BlkTransactionState *common,
Error **errp)
{
- int flags, ret;
- QDict *options;
+ int flags = 0, ret;
+ QDict *options = NULL;
Error *local_err = NULL;
- bool has_device = false;
+ /* Device and node name of the image to generate the snapshot from */
const char *device;
- bool has_node_name = false;
const char *node_name;
- bool has_snapshot_node_name = false;
- const char *snapshot_node_name;
+ /* Reference to the new image (for 'blockdev-snapshot') */
+ const char *snapshot_ref;
+ /* File name of the new image (for 'blockdev-snapshot-sync') */
const char *new_image_file;
- const char *format = "qcow2";
- enum NewImageMode mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
ExternalSnapshotState *state =
DO_UPCAST(ExternalSnapshotState, common, common);
TransactionAction *action = common->action;
- /* get parameters */
- g_assert(action->type == TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC);
-
- has_device = action->u.blockdev_snapshot_sync->has_device;
- device = action->u.blockdev_snapshot_sync->device;
- has_node_name = action->u.blockdev_snapshot_sync->has_node_name;
- node_name = action->u.blockdev_snapshot_sync->node_name;
- has_snapshot_node_name =
- action->u.blockdev_snapshot_sync->has_snapshot_node_name;
- snapshot_node_name = action->u.blockdev_snapshot_sync->snapshot_node_name;
-
- new_image_file = action->u.blockdev_snapshot_sync->snapshot_file;
- if (action->u.blockdev_snapshot_sync->has_format) {
- format = action->u.blockdev_snapshot_sync->format;
- }
- if (action->u.blockdev_snapshot_sync->has_mode) {
- mode = action->u.blockdev_snapshot_sync->mode;
+ /* 'blockdev-snapshot' and 'blockdev-snapshot-sync' have similar
+ * purpose but a different set of parameters */
+ switch (action->type) {
+ case TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT:
+ {
+ BlockdevSnapshot *s = action->u.blockdev_snapshot;
+ device = s->node;
+ node_name = s->node;
+ new_image_file = NULL;
+ snapshot_ref = s->overlay;
+ }
+ break;
+ case TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC:
+ {
+ BlockdevSnapshotSync *s = action->u.blockdev_snapshot_sync;
+ device = s->has_device ? s->device : NULL;
+ node_name = s->has_node_name ? s->node_name : NULL;
+ new_image_file = s->snapshot_file;
+ snapshot_ref = NULL;
+ }
+ break;
+ default:
+ g_assert_not_reached();
}
/* start processing */
- state->old_bs = bdrv_lookup_bs(has_device ? device : NULL,
- has_node_name ? node_name : NULL,
- &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- return;
- }
-
- if (has_node_name && !has_snapshot_node_name) {
- error_setg(errp, "New snapshot node name missing");
- return;
- }
-
- if (has_snapshot_node_name &&
- bdrv_lookup_bs(snapshot_node_name, snapshot_node_name, NULL)) {
- error_setg(errp, "New snapshot node name already in use");
+ state->old_bs = bdrv_lookup_bs(device, node_name, errp);
+ if (!state->old_bs) {
return;
}
@@ -1602,35 +1604,70 @@ static void
external_snapshot_prepare(BlkTransactionState *common,
return;
}
- flags = state->old_bs->open_flags;
+ if (action->type == TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC) {
+ BlockdevSnapshotSync *s = action->u.blockdev_snapshot_sync;
+ const char *format = s->has_format ? s->format : "qcow2";
+ enum NewImageMode mode;
+ const char *snapshot_node_name =
+ s->has_snapshot_node_name ? s->snapshot_node_name : NULL;
- /* create new image w/backing file */
- if (mode != NEW_IMAGE_MODE_EXISTING) {
- bdrv_img_create(new_image_file, format,
- state->old_bs->filename,
- state->old_bs->drv->format_name,
- NULL, -1, flags, &local_err, false);
- if (local_err) {
- error_propagate(errp, local_err);
+ if (node_name && !snapshot_node_name) {
+ error_setg(errp, "New snapshot node name missing");
return;
}
- }
- options = qdict_new();
- if (has_snapshot_node_name) {
- qdict_put(options, "node-name",
- qstring_from_str(snapshot_node_name));
+ if (snapshot_node_name &&
+ bdrv_lookup_bs(snapshot_node_name, snapshot_node_name, NULL)) {
+ error_setg(errp, "New snapshot node name already in use");
+ return;
+ }
+
+ flags = state->old_bs->open_flags;
+
+ /* create new image w/backing file */
+ mode = s->has_mode ? s->mode : NEW_IMAGE_MODE_ABSOLUTE_PATHS;
+ if (mode != NEW_IMAGE_MODE_EXISTING) {
+ bdrv_img_create(new_image_file, format,
+ state->old_bs->filename,
+ state->old_bs->drv->format_name,
+ NULL, -1, flags, &local_err, false);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+ }
+
+ options = qdict_new();
+ if (s->has_snapshot_node_name) {
+ qdict_put(options, "node-name",
+ qstring_from_str(snapshot_node_name));
+ }
+ qdict_put(options, "driver", qstring_from_str(format));
+
+ flags |= BDRV_O_NO_BACKING;
}
- qdict_put(options, "driver", qstring_from_str(format));
- /* TODO Inherit bs->options or only take explicit options with an
- * extended QMP command? */
assert(state->new_bs == NULL);
- ret = bdrv_open(&state->new_bs, new_image_file, NULL, options,
- flags | BDRV_O_NO_BACKING, &local_err);
+ ret = bdrv_open(&state->new_bs, new_image_file, snapshot_ref, options,
+ flags, errp);
/* We will manually add the backing_hd field to the bs later */
if (ret != 0) {
- error_propagate(errp, local_err);
+ return;
+ }
+
+ if (state->new_bs->blk != NULL) {
+ error_setg(errp, "The snapshot is already in use by %s",
+ blk_name(state->new_bs->blk));
+ return;
+ }
+
+ if (bdrv_op_is_blocked(state->new_bs, BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT,
+ errp)) {
+ return;
+ }
+
+ if (state->new_bs->backing != NULL) {
+ error_setg(errp, "The snapshot already has a backing image");
}
}
@@ -1833,6 +1870,12 @@ static void abort_commit(BlkTransactionState *common)
}
static const BdrvActionOps actions[] = {
+ [TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT] = {
+ .instance_size = sizeof(ExternalSnapshotState),
+ .prepare = external_snapshot_prepare,
+ .commit = external_snapshot_commit,
+ .abort = external_snapshot_abort,
+ },
[TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC] = {
.instance_size = sizeof(ExternalSnapshotState),
.prepare = external_snapshot_prepare,
diff --git a/qapi-schema.json b/qapi-schema.json
index 60827a4..2d8fa7c 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -1531,9 +1531,11 @@
# abort since 1.6
# blockdev-snapshot-internal-sync since 1.7
# blockdev-backup since 2.3
+# blockdev-snapshot since 2.5
##
{ 'union': 'TransactionAction',
'data': {
+ 'blockdev-snapshot': 'BlockdevSnapshot',
'blockdev-snapshot-sync': 'BlockdevSnapshotSync',
'drive-backup': 'DriveBackup',
'blockdev-backup': 'BlockdevBackup',
diff --git a/qapi/block-core.json b/qapi/block-core.json
index f592adc..083d2cd 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -705,6 +705,21 @@
'*format': 'str', '*mode': 'NewImageMode' } }
##
+# @BlockdevSnapshot
+#
+# @node: device or node name that will have a snapshot created.
+#
+# @overlay: reference to the existing block device that will become
+# the overlay of @node, as part of creating the snapshot.
+# It must not have a current backing file (this can be
+# achieved by passing "backing": "" to blockdev-add).
+#
+# Since 2.5
+##
+{ 'struct': 'BlockdevSnapshot',
+ 'data': { 'node': 'str', 'overlay': 'str' } }
+
+##
# @DriveBackup
#
# @device: the name of the device which should be copied.
@@ -800,6 +815,19 @@
{ 'command': 'blockdev-snapshot-sync',
'data': 'BlockdevSnapshotSync' }
+
+##
+# @blockdev-snapshot
+#
+# Generates a snapshot of a block device.
+#
+# For the arguments, see the documentation of BlockdevSnapshot.
+#
+# Since 2.5
+##
+{ 'command': 'blockdev-snapshot',
+ 'data': 'BlockdevSnapshot' }
+
##
# @change-backing-file
#
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 707e0bd..bd4d38f 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -1476,6 +1476,44 @@ Example:
EQMP
{
+ .name = "blockdev-snapshot",
+ .args_type = "node:s,overlay:s",
+ .mhandler.cmd_new = qmp_marshal_blockdev_snapshot,
+ },
+
+SQMP
+blockdev-snapshot
+-----------------
+Since 2.5
+
+Create a snapshot, by installing 'node' as the backing image of
+'overlay'. Additionally, if 'node' is associated with a block
+device, the block device changes to using 'overlay' as its new active
+image.
+
+Arguments:
+
+- "node": device that will have a snapshot created (json-string)
+- "overlay": device that will have 'node' as its backing image (json-string)
+
+Example:
+
+-> { "execute": "blockdev-add",
+ "arguments": { "options": { "driver": "qcow2",
+ "node-name": "node1534",
+ "file": { "driver": "file",
+ "filename": "hd1.qcow2"
},
+ "backing": "" } } }
+
+<- { "return": {} }
+
+-> { "execute": "blockdev-snapshot", "arguments": { "node": "ide-hd0",
+ "overlay": "node1534" } }
+<- { "return": {} }
+
+EQMP
+
+ {
.name = "blockdev-snapshot-internal-sync",
.args_type = "device:B,name:s",
.mhandler.cmd_new = qmp_marshal_blockdev_snapshot_internal_sync,
--
1.8.3.1
- [Qemu-block] [PULL 07/37] blockdev: Add blockdev-remove-medium, (continued)
- [Qemu-block] [PULL 07/37] blockdev: Add blockdev-remove-medium, Kevin Wolf, 2015/11/05
- [Qemu-block] [PULL 08/37] blockdev: Add blockdev-insert-medium, Kevin Wolf, 2015/11/05
- [Qemu-block] [PULL 10/37] blockdev: Implement change with basic operations, Kevin Wolf, 2015/11/05
- [Qemu-block] [PULL 11/37] block: Inquire tray state before tray-moved events, Kevin Wolf, 2015/11/05
- [Qemu-block] [PULL 12/37] qmp: Introduce blockdev-change-medium, Kevin Wolf, 2015/11/05
- [Qemu-block] [PULL 14/37] blockdev: read-only-mode for blockdev-change-medium, Kevin Wolf, 2015/11/05
- [Qemu-block] [PULL 15/37] hmp: Add read-only-mode option to change command, Kevin Wolf, 2015/11/05
- [Qemu-block] [PULL 17/37] block: check for existing device IDs in external_snapshot_prepare(), Kevin Wolf, 2015/11/05
- [Qemu-block] [PULL 09/37] blockdev: Implement eject with basic operations, Kevin Wolf, 2015/11/05
- [Qemu-block] [PULL 13/37] hmp: Use blockdev-change-medium for change command, Kevin Wolf, 2015/11/05
- [Qemu-block] [PULL 20/37] block: add a 'blockdev-snapshot' QMP command,
Kevin Wolf <=
- [Qemu-block] [PULL 16/37] iotests: Add test for change-related QMP commands, Kevin Wolf, 2015/11/05
- [Qemu-block] [PULL 21/37] block: add tests for the 'blockdev-snapshot' command, Kevin Wolf, 2015/11/05
- [Qemu-block] [PULL 18/37] block: rename BlockdevSnapshot to BlockdevSnapshotSync, Kevin Wolf, 2015/11/05
- [Qemu-block] [PULL 19/37] block: support passing 'backing': '' to 'blockdev-add', Kevin Wolf, 2015/11/05
- [Qemu-block] [PULL 22/37] commit: reopen overlay_bs before base, Kevin Wolf, 2015/11/05
- [Qemu-block] [PULL 23/37] qemu-iotests: Test the reopening of overlay_bs in 'block-commit', Kevin Wolf, 2015/11/05
- [Qemu-block] [PULL 25/37] qemu-img: add check for zero-length job len, Kevin Wolf, 2015/11/05
- [Qemu-block] [PULL 24/37] qcow2: avoid misaligned 64bit bswap, Kevin Wolf, 2015/11/05
- [Qemu-block] [PULL 30/37] block: test 'blockdev-snapshot' using a file BDS as the overlay, Kevin Wolf, 2015/11/05
- [Qemu-block] [PULL 26/37] throttle: Check for pending requests in throttle_group_unregister_bs(), Kevin Wolf, 2015/11/05