提交 bbe86010 编写于 作者: W Wenchao Xia 提交者: Kevin Wolf

qmp: add internal snapshot support in qmp_transaction

Unlike savevm, the qmp_transaction interface will not generate
snapshot name automatically, saving trouble to return information
of the new created snapshot.

Although qcow2 support storing multiple snapshots with same name
but different ID, here it will fail when an snapshot with that name
already exist before the operation. Format such as rbd do not support
ID at all, and in most case, it means trouble to user when he faces
multiple snapshots with same name, so ban that case. Request with
empty name will be rejected.

Snapshot ID can't be specified in this interface.
Signed-off-by: NWenchao Xia <xiawenc@linux.vnet.ibm.com>
Signed-off-by: NKevin Wolf <kwolf@redhat.com>
上级 a89d89d3
...@@ -889,6 +889,117 @@ struct BlkTransactionState { ...@@ -889,6 +889,117 @@ struct BlkTransactionState {
QSIMPLEQ_ENTRY(BlkTransactionState) entry; QSIMPLEQ_ENTRY(BlkTransactionState) entry;
}; };
/* internal snapshot private data */
typedef struct InternalSnapshotState {
BlkTransactionState common;
BlockDriverState *bs;
QEMUSnapshotInfo sn;
} InternalSnapshotState;
static void internal_snapshot_prepare(BlkTransactionState *common,
Error **errp)
{
const char *device;
const char *name;
BlockDriverState *bs;
QEMUSnapshotInfo old_sn, *sn;
bool ret;
qemu_timeval tv;
BlockdevSnapshotInternal *internal;
InternalSnapshotState *state;
int ret1;
g_assert(common->action->kind ==
TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC);
internal = common->action->blockdev_snapshot_internal_sync;
state = DO_UPCAST(InternalSnapshotState, common, common);
/* 1. parse input */
device = internal->device;
name = internal->name;
/* 2. check for validation */
bs = bdrv_find(device);
if (!bs) {
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
return;
}
if (!bdrv_is_inserted(bs)) {
error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
return;
}
if (bdrv_is_read_only(bs)) {
error_set(errp, QERR_DEVICE_IS_READ_ONLY, device);
return;
}
if (!bdrv_can_snapshot(bs)) {
error_set(errp, QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED,
bs->drv->format_name, device, "internal snapshot");
return;
}
if (!strlen(name)) {
error_setg(errp, "Name is empty");
return;
}
/* check whether a snapshot with name exist */
ret = bdrv_snapshot_find_by_id_and_name(bs, NULL, name, &old_sn, errp);
if (error_is_set(errp)) {
return;
} else if (ret) {
error_setg(errp,
"Snapshot with name '%s' already exists on device '%s'",
name, device);
return;
}
/* 3. take the snapshot */
sn = &state->sn;
pstrcpy(sn->name, sizeof(sn->name), name);
qemu_gettimeofday(&tv);
sn->date_sec = tv.tv_sec;
sn->date_nsec = tv.tv_usec * 1000;
sn->vm_clock_nsec = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
ret1 = bdrv_snapshot_create(bs, sn);
if (ret1 < 0) {
error_setg_errno(errp, -ret1,
"Failed to create snapshot '%s' on device '%s'",
name, device);
return;
}
/* 4. succeed, mark a snapshot is created */
state->bs = bs;
}
static void internal_snapshot_abort(BlkTransactionState *common)
{
InternalSnapshotState *state =
DO_UPCAST(InternalSnapshotState, common, common);
BlockDriverState *bs = state->bs;
QEMUSnapshotInfo *sn = &state->sn;
Error *local_error = NULL;
if (!bs) {
return;
}
if (bdrv_snapshot_delete(bs, sn->id_str, sn->name, &local_error) < 0) {
error_report("Failed to delete snapshot with id '%s' and name '%s' on "
"device '%s' in abort: %s",
sn->id_str,
sn->name,
bdrv_get_device_name(bs),
error_get_pretty(local_error));
error_free(local_error);
}
}
/* external snapshot private data */ /* external snapshot private data */
typedef struct ExternalSnapshotState { typedef struct ExternalSnapshotState {
BlkTransactionState common; BlkTransactionState common;
...@@ -1072,6 +1183,11 @@ static const BdrvActionOps actions[] = { ...@@ -1072,6 +1183,11 @@ static const BdrvActionOps actions[] = {
.prepare = abort_prepare, .prepare = abort_prepare,
.commit = abort_commit, .commit = abort_commit,
}, },
[TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC] = {
.instance_size = sizeof(InternalSnapshotState),
.prepare = internal_snapshot_prepare,
.abort = internal_snapshot_abort,
},
}; };
/* /*
......
...@@ -1685,6 +1685,22 @@ ...@@ -1685,6 +1685,22 @@
'data': { 'device': 'str', 'snapshot-file': 'str', '*format': 'str', 'data': { 'device': 'str', 'snapshot-file': 'str', '*format': 'str',
'*mode': 'NewImageMode' } } '*mode': 'NewImageMode' } }
##
# @BlockdevSnapshotInternal
#
# @device: the name of the device to generate the snapshot from
#
# @name: the name of the internal snapshot to be created
#
# Notes: In transaction, if @name is empty, or any snapshot matching @name
# exists, the operation will fail. Only some image formats support it,
# for example, qcow2, rbd, and sheepdog.
#
# Since: 1.7
##
{ 'type': 'BlockdevSnapshotInternal',
'data': { 'device': 'str', 'name': 'str' } }
## ##
# @DriveBackup # @DriveBackup
# #
...@@ -1747,7 +1763,8 @@ ...@@ -1747,7 +1763,8 @@
'data': { 'data': {
'blockdev-snapshot-sync': 'BlockdevSnapshot', 'blockdev-snapshot-sync': 'BlockdevSnapshot',
'drive-backup': 'DriveBackup', 'drive-backup': 'DriveBackup',
'abort': 'Abort' 'abort': 'Abort',
'blockdev-snapshot-internal-sync': 'BlockdevSnapshotInternal'
} } } }
## ##
......
...@@ -1001,14 +1001,15 @@ SQMP ...@@ -1001,14 +1001,15 @@ SQMP
transaction transaction
----------- -----------
Atomically operate on one or more block devices. The only supported Atomically operate on one or more block devices. The only supported operations
operation for now is snapshotting. If there is any failure performing for now are drive-backup, internal and external snapshotting. A list of
any of the operations, all snapshots for the group are abandoned, and dictionaries is accepted, that contains the actions to be performed.
the original disks pre-snapshot attempt are used. If there is any failure performing any of the operations, all operations
for the group are abandoned.
A list of dictionaries is accepted, that contains the actions to be performed. For external snapshots, the dictionary contains the device, the file to use for
For snapshots this is the device, the file to use for the new snapshot, the new snapshot, and the format. The default format, if not specified, is
and the format. The default format, if not specified, is qcow2. qcow2.
Each new snapshot defaults to being created by QEMU (wiping any Each new snapshot defaults to being created by QEMU (wiping any
contents if the file already exists), but it is also possible to reuse contents if the file already exists), but it is also possible to reuse
...@@ -1017,6 +1018,17 @@ the new image file has the same contents as the current one; QEMU cannot ...@@ -1017,6 +1018,17 @@ the new image file has the same contents as the current one; QEMU cannot
perform any meaningful check. Typically this is achieved by using the perform any meaningful check. Typically this is achieved by using the
current image file as the backing file for the new image. current image file as the backing file for the new image.
On failure, the original disks pre-snapshot attempt will be used.
For internal snapshots, the dictionary contains the device and the snapshot's
name. If an internal snapshot matching name already exists, the request will
be rejected. Only some image formats support it, for example, qcow2, rbd,
and sheepdog.
On failure, qemu will try delete the newly created internal snapshot in the
transaction. When an I/O error occurs during deletion, the user needs to fix
it later with qemu-img or other command.
Arguments: Arguments:
actions array: actions array:
...@@ -1029,6 +1041,9 @@ actions array: ...@@ -1029,6 +1041,9 @@ actions array:
- "format": format of new image (json-string, optional) - "format": format of new image (json-string, optional)
- "mode": whether and how QEMU should create the snapshot file - "mode": whether and how QEMU should create the snapshot file
(NewImageMode, optional, default "absolute-paths") (NewImageMode, optional, default "absolute-paths")
When "type" is "blockdev-snapshot-internal-sync":
- "device": device name to snapshot (json-string)
- "name": name of the new snapshot (json-string)
Example: Example:
...@@ -1040,7 +1055,10 @@ Example: ...@@ -1040,7 +1055,10 @@ Example:
{ 'type': 'blockdev-snapshot-sync', 'data' : { "device": "ide-hd1", { 'type': 'blockdev-snapshot-sync', 'data' : { "device": "ide-hd1",
"snapshot-file": "/some/place/my-image2", "snapshot-file": "/some/place/my-image2",
"mode": "existing", "mode": "existing",
"format": "qcow2" } } ] } } "format": "qcow2" } },
{ 'type': 'blockdev-snapshot-internal-sync', 'data' : {
"device": "ide-hd2",
"name": "snapshot0" } } ] } }
<- { "return": {} } <- { "return": {} }
EQMP EQMP
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册