qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Kevin Wolf <kwolf@redhat.com>
To: anthony@codemonkey.ws
Cc: kwolf@redhat.com, qemu-devel@nongnu.org
Subject: [Qemu-devel] [PULL 16/33] qmp: add internal snapshot support in qmp_transaction
Date: Fri, 13 Sep 2013 13:50:46 +0200	[thread overview]
Message-ID: <1379073063-14963-17-git-send-email-kwolf@redhat.com> (raw)
In-Reply-To: <1379073063-14963-1-git-send-email-kwolf@redhat.com>

From: Wenchao Xia <xiawenc@linux.vnet.ibm.com>

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: Wenchao Xia <xiawenc@linux.vnet.ibm.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 blockdev.c       | 116 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 qapi-schema.json |  19 ++++++++-
 qmp-commands.hx  |  34 ++++++++++++----
 3 files changed, 160 insertions(+), 9 deletions(-)

diff --git a/blockdev.c b/blockdev.c
index 07dac05..0fd30e2 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -889,6 +889,117 @@ struct BlkTransactionState {
     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 */
 typedef struct ExternalSnapshotState {
     BlkTransactionState common;
@@ -1072,6 +1183,11 @@ static const BdrvActionOps actions[] = {
         .prepare = abort_prepare,
         .commit = abort_commit,
     },
+    [TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC] = {
+        .instance_size = sizeof(InternalSnapshotState),
+        .prepare  = internal_snapshot_prepare,
+        .abort = internal_snapshot_abort,
+    },
 };
 
 /*
diff --git a/qapi-schema.json b/qapi-schema.json
index 2b2c8bc..77bbbf5 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -1686,6 +1686,22 @@
             '*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
 #
 # @device: the name of the device which should be copied.
@@ -1747,7 +1763,8 @@
   'data': {
        'blockdev-snapshot-sync': 'BlockdevSnapshot',
        'drive-backup': 'DriveBackup',
-       'abort': 'Abort'
+       'abort': 'Abort',
+       'blockdev-snapshot-internal-sync': 'BlockdevSnapshotInternal'
    } }
 
 ##
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 008cad9..6670192 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -1001,14 +1001,15 @@ SQMP
 transaction
 -----------
 
-Atomically operate on one or more block devices.  The only supported
-operation for now is snapshotting.  If there is any failure performing
-any of the operations, all snapshots for the group are abandoned, and
-the original disks pre-snapshot attempt are used.
+Atomically operate on one or more block devices.  The only supported operations
+for now are drive-backup, internal and external snapshotting.  A list of
+dictionaries is accepted, that contains the actions to be performed.
+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 snapshots this is the device, the file to use for the new snapshot,
-and the format.  The default format, if not specified, is qcow2.
+For external snapshots, the dictionary contains the device, the file to use for
+the new snapshot, and the format.  The default format, if not specified, is
+qcow2.
 
 Each new snapshot defaults to being created by QEMU (wiping any
 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
 perform any meaningful check.  Typically this is achieved by using the
 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:
 
 actions array:
@@ -1029,6 +1041,9 @@ actions array:
       - "format": format of new image (json-string, optional)
       - "mode": whether and how QEMU should create the snapshot file
         (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:
 
@@ -1040,7 +1055,10 @@ Example:
          { 'type': 'blockdev-snapshot-sync', 'data' : { "device": "ide-hd1",
                                          "snapshot-file": "/some/place/my-image2",
                                          "mode": "existing",
-                                         "format": "qcow2" } } ] } }
+                                         "format": "qcow2" } },
+         { 'type': 'blockdev-snapshot-internal-sync', 'data' : {
+                                         "device": "ide-hd2",
+                                         "name": "snapshot0" } } ] } }
 <- { "return": {} }
 
 EQMP
-- 
1.8.1.4

  parent reply	other threads:[~2013-09-13 11:51 UTC|newest]

Thread overview: 36+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-09-13 11:50 [Qemu-devel] [PULL 00/33] Block patches Kevin Wolf
2013-09-13 11:50 ` [Qemu-devel] [PULL 01/33] qcow2: Pass discard type to qcow2_discard_clusters() Kevin Wolf
2013-09-13 11:50 ` [Qemu-devel] [PULL 02/33] qcow2: Discard VM state in active L1 after creating snapshot Kevin Wolf
2013-09-13 11:50 ` [Qemu-devel] [PULL 03/33] raw-win32.c: Fix incorrect handling behaviour of small block files Kevin Wolf
2013-09-13 11:50 ` [Qemu-devel] [PULL 04/33] block: Image file option amendment Kevin Wolf
2013-09-13 11:50 ` [Qemu-devel] [PULL 05/33] qcow2-cache: Empty cache Kevin Wolf
2013-09-13 11:50 ` [Qemu-devel] [PULL 06/33] qcow2-cluster: Expand zero clusters Kevin Wolf
2013-09-13 11:50 ` [Qemu-devel] [PULL 07/33] qcow2: Save refcount order in BDRVQcowState Kevin Wolf
2013-09-13 11:50 ` [Qemu-devel] [PULL 08/33] qcow2: Implement bdrv_amend_options Kevin Wolf
2013-09-13 11:50 ` [Qemu-devel] [PULL 09/33] qemu-iotest: qcow2 image option amendment Kevin Wolf
2013-09-13 11:50 ` [Qemu-devel] [PULL 10/33] qemu-iotests: add unix socket help program Kevin Wolf
2013-09-13 11:50 ` [Qemu-devel] [PULL 11/33] qemu-iotests: add infrastructure of fd passing via SCM Kevin Wolf
2013-09-13 11:50 ` [Qemu-devel] [PULL 12/33] qemu-iotests: add tests for runtime fd passing via SCM rights Kevin Wolf
2013-09-13 18:30   ` Eric Blake
2013-09-13 11:50 ` [Qemu-devel] [PULL 13/33] qemu-iotests: New test case in 061 Kevin Wolf
2013-09-13 11:50 ` [Qemu-devel] [PULL 14/33] snapshot: new function bdrv_snapshot_find_by_id_and_name() Kevin Wolf
2013-09-13 11:50 ` [Qemu-devel] [PULL 15/33] snapshot: distinguish id and name in snapshot delete Kevin Wolf
2013-09-13 18:42   ` Eric Blake
2013-09-13 11:50 ` Kevin Wolf [this message]
2013-09-13 11:50 ` [Qemu-devel] [PULL 17/33] qmp: add interface blockdev-snapshot-internal-sync Kevin Wolf
2013-09-13 11:50 ` [Qemu-devel] [PULL 18/33] qmp: add interface blockdev-snapshot-delete-internal-sync Kevin Wolf
2013-09-13 11:50 ` [Qemu-devel] [PULL 19/33] hmp: add interface hmp_snapshot_blkdev_internal Kevin Wolf
2013-09-13 11:50 ` [Qemu-devel] [PULL 20/33] hmp: add interface hmp_snapshot_delete_blkdev_internal Kevin Wolf
2013-09-13 11:50 ` [Qemu-devel] [PULL 21/33] qemu-iotests: add 057 internal snapshot for block device test case Kevin Wolf
2013-09-13 11:50 ` [Qemu-devel] [PULL 22/33] bdrv: Use "Error" for opening images Kevin Wolf
2013-09-13 11:50 ` [Qemu-devel] [PULL 23/33] bdrv: Use "Error" for creating images Kevin Wolf
2013-09-13 11:50 ` [Qemu-devel] [PULL 24/33] block: Error parameter for open functions Kevin Wolf
2013-09-13 11:50 ` [Qemu-devel] [PULL 25/33] block: Error parameter for create functions Kevin Wolf
2013-09-13 11:50 ` [Qemu-devel] [PULL 26/33] qemu-img create: Emit filename on error Kevin Wolf
2013-09-13 11:50 ` [Qemu-devel] [PULL 27/33] qcow2: Use Error parameter Kevin Wolf
2013-09-13 11:50 ` [Qemu-devel] [PULL 28/33] qemu-iotests: Adjustments due to error propagation Kevin Wolf
2013-09-13 11:50 ` [Qemu-devel] [PULL 29/33] coroutine: add ./configure --disable-coroutine-pool Kevin Wolf
2013-09-13 11:51 ` [Qemu-devel] [PULL 30/33] qemu-img: fix invalid JSON Kevin Wolf
2013-09-13 11:51 ` [Qemu-devel] [PULL 31/33] qemu-iotests: Cleanup test image in test number 007 Kevin Wolf
2013-09-13 11:51 ` [Qemu-devel] [PULL 32/33] block: Assert validity of BdrvActionOps Kevin Wolf
2013-09-13 11:51 ` [Qemu-devel] [PULL 33/33] qemu-iotests: Fix test 038 Kevin Wolf

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1379073063-14963-17-git-send-email-kwolf@redhat.com \
    --to=kwolf@redhat.com \
    --cc=anthony@codemonkey.ws \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).