qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH 2/3] qapi: Introduce blockdev-group-snapshot-sync co


From: Jeff Cody
Subject: [Qemu-devel] [PATCH 2/3] qapi: Introduce blockdev-group-snapshot-sync command
Date: Mon, 20 Feb 2012 12:31:11 -0500

This is a QAPI/QMP only command to take a snapshot of a group of
devices. This is simlar to the blockdev-snapshot-sync command, except
blockdev-group-snapshot-sync accepts a list devices, filenames, and
formats.

It is attempted to keep the snapshot of the group atomic; if
any snapshot of a device in a given group fails, then the whole group
is reverted back to its original image, and error is reported.

This allows the group of disks to remain consistent with each other,
even across snapshot failures.

Signed-off-by: Jeff Cody <address@hidden>
---
 blockdev.c       |  130 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 qapi-schema.json |   45 +++++++++++++++++++
 qmp-commands.hx  |   39 ++++++++++++++++
 3 files changed, 214 insertions(+), 0 deletions(-)

diff --git a/blockdev.c b/blockdev.c
index 05e7c5e..0149720 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -714,6 +714,136 @@ void qmp_blockdev_snapshot_sync(const char *device, const 
char *snapshot_file,
     }
 }
 
+
+typedef struct BlkGroupSnapshotData {
+    BlockDriverState *bs;
+    BlockDriver *drv;
+    BlockDriver *old_drv;
+    int flags;
+    const char *format;
+    char old_filename[1024];
+    const char *snapshot_file;
+    bool has_pivoted;
+    QSIMPLEQ_ENTRY(BlkGroupSnapshotData) entry;
+} BlkGroupSnapshotData;
+
+/*
+ * 'Atomic' group snapshots.  The snapshots are taken as a set, and if any fail
+ *  then we attempt to undo all of the pivots performed.
+ */
+void qmp_blockdev_group_snapshot_sync(SnapshotDevList *dev_list,
+                                                   Error **errp)
+{
+    int ret = 0;
+    SnapshotDevList *dev_entry = dev_list;
+    SnapshotDev *dev_info = NULL;
+    BlkGroupSnapshotData *snap_entry;
+    BlockDriver *proto_drv;
+
+    QSIMPLEQ_HEAD(gsnp_list, BlkGroupSnapshotData) gsnp_list;
+    QSIMPLEQ_INIT(&gsnp_list);
+
+    /* We don't do anything in this loop that commits us to the snapshot */
+    while (NULL != dev_entry) {
+        dev_info = dev_entry->value;
+        dev_entry = dev_entry->next;
+
+        snap_entry = g_malloc0(sizeof(BlkGroupSnapshotData));
+
+        snap_entry->bs = bdrv_find(dev_info->device);
+
+        if (!snap_entry->bs) {
+            error_set(errp, QERR_DEVICE_NOT_FOUND, dev_info->device);
+            goto exit;
+        }
+        if (bdrv_in_use(snap_entry->bs)) {
+            error_set(errp, QERR_DEVICE_IN_USE, dev_info->device);
+            goto exit;
+        }
+
+        pstrcpy(snap_entry->old_filename, sizeof(snap_entry->old_filename),
+                snap_entry->bs->filename);
+
+        snap_entry->snapshot_file = dev_info->snapshot_file;
+        snap_entry->old_drv = snap_entry->bs->drv;
+        snap_entry->flags = snap_entry->bs->open_flags;
+
+        if (!dev_info->has_format) {
+            snap_entry->format = "qcow2";
+        } else {
+            snap_entry->format = dev_info->format;
+        }
+
+        snap_entry->drv = bdrv_find_format(snap_entry->format);
+        if (!snap_entry->drv) {
+            error_set(errp, QERR_INVALID_BLOCK_FORMAT, snap_entry->format);
+            goto exit;
+        }
+
+        proto_drv = bdrv_find_protocol(snap_entry->snapshot_file);
+        if (!proto_drv) {
+            error_set(errp, QERR_INVALID_BLOCK_FORMAT, snap_entry->format);
+            goto exit;
+        }
+        ret = bdrv_img_create(snap_entry->snapshot_file, snap_entry->format,
+                              snap_entry->bs->filename,
+                              snap_entry->bs->drv->format_name, NULL, -1,
+                              snap_entry->flags);
+        if (ret) {
+            error_set(errp, QERR_UNDEFINED_ERROR);
+            goto exit;
+        }
+        snap_entry->has_pivoted = false;
+        QSIMPLEQ_INSERT_TAIL(&gsnp_list, snap_entry, entry);
+    }
+
+    /*
+     * Now we will drain, flush, & pivot everything - if any of the pivots 
fail,
+     * we will flag an error, and attempt to rollback.
+     */
+    bdrv_drain_all();
+    QSIMPLEQ_FOREACH(snap_entry, &gsnp_list, entry) {
+        bdrv_flush(snap_entry->bs);
+        bdrv_close(snap_entry->bs);
+
+        ret = bdrv_open(snap_entry->bs, snap_entry->snapshot_file,
+                        snap_entry->flags, snap_entry->drv);
+        snap_entry->has_pivoted = true;
+        /*
+         * If we fail any of these, then we need to rollback all that we have
+         * performed up to this point
+         */
+        if (ret != 0) {
+            error_set(errp, QERR_OPEN_FILE_FAILED, snap_entry->snapshot_file);
+            goto error_rollback;
+        }
+    }
+
+    /* success */
+    goto exit;
+
+error_rollback:
+    /* failure, undo everything as much as we can */
+    QSIMPLEQ_FOREACH(snap_entry, &gsnp_list, entry) {
+        if (snap_entry->has_pivoted) {
+            ret = bdrv_open(snap_entry->bs, snap_entry->old_filename,
+                            snap_entry->flags, snap_entry->old_drv);
+            if (ret != 0) {
+                /* This is very very bad */
+                error_set(errp, QERR_OPEN_FILE_FAILED,
+                          snap_entry->old_filename);
+            }
+        }
+    }
+exit:
+    QSIMPLEQ_FOREACH(snap_entry, &gsnp_list, entry) {
+        g_free(snap_entry);
+    }
+
+    return;
+}
+
+
 static void eject_device(BlockDriverState *bs, int force, Error **errp)
 {
     if (bdrv_in_use(bs)) {
diff --git a/qapi-schema.json b/qapi-schema.json
index 80debe6..b8d66d0 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -1107,6 +1107,51 @@
 { 'command': 'block_resize', 'data': { 'device': 'str', 'size': 'int' }}
 
 ##
+# @SnapshotDev
+#
+# @device:  the name of the device to generate the snapshot from.
+#
+# @snapshot-file: the target of the new image. If the file exists, or if it
+#                 is a device, the snapshot will be created in the existing
+#                 file/device. If does not exist, a new file will be created.
+#
+# @format: #optional the format of the snapshot image, default is 'qcow2'.
+##
+{ 'type': 'SnapshotDev',
+  'data': {'device': 'str', 'snapshot-file': 'str', '*format': 'str' } }
+
+##
+# @blockdev-group-snapshot-sync
+#
+# Generates a synchronous snapshot of a group of one or more block devices,
+# as atomically as possible.
+#
+#  List of:
+#  @SnapshotDev: information needed for the device snapshot
+#
+# Returns: nothing on success
+#          If @device is not a valid block device, DeviceNotFound
+#          If @snapshot-file can't be opened, OpenFileFailed
+#          If @format is invalid, InvalidBlockFormat
+#
+# Notes: One of the last steps taken by this command is to close the current
+#        images being used by each specified @device and open the corresponding
+#        @snapshot-file one. If that fails, the command will try to reopen
+#        all of the original image files, for each device specified. If
+#        that also fails OpenFileFailed will be returned and the guest may get
+#        unexpected errors.
+#
+#        Since there could potentially be more than one file open or drive
+#        failures, the additional command 
'blockdev-query-group-snapshot-failure'
+#        will return a list of all device files that have failed.  This could
+#        include the original filename if the reopen of an original image file
+#        failed.
+#
+##
+{ 'command': 'blockdev-group-snapshot-sync',
+  'data': { 'dev': [ 'SnapshotDev' ] } }
+
+##
 # @blockdev-snapshot-sync
 #
 # Generates a synchronous snapshot of a block device.
diff --git a/qmp-commands.hx b/qmp-commands.hx
index bd6b641..2fe1e6e 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -665,6 +665,45 @@ EQMP
         .args_type  = "device:B",
         .mhandler.cmd_new = qmp_marshal_input_block_job_cancel,
     },
+    {
+        .name       = "blockdev-group-snapshot-sync",
+        .args_type  = "device:B,snapshot-file:s,format:s?",
+        .mhandler.cmd_new = qmp_marshal_input_blockdev_group_snapshot_sync,
+        .flags      = MONITOR_CMD_ARRAY_INPUT,
+    },
+
+SQMP
+blockdev-group-snapshot-sync
+----------------------
+
+Synchronous snapshot of one or more block devices.  A list array input
+is accepted, that contains the device, snapshot-file to be create as the
+target of the new image. If the file exists, or if it is a device, the
+snapshot will be created in the existing file/device. If does not
+exist, a new file will be created. format specifies the format of the
+snapshot image, default is qcow2.  On failure of any device, it is
+attempted to reopen the original image for all the devices that were
+specified.
+
+Arguments:
+
+- "device": device name to snapshot (json-string)
+- "snapshot-file": name of new image file (json-string)
+- "format": format of new image (json-string, optional)
+
+Example:
+
+-> { "execute": "blockdev-group-snapshot-sync", "arguments": [{ "device": 
"ide-hd0",
+                                                                
"snapshot-file":
+                                                                
"/some/place/my-image",
+                                                                "format": 
"qcow2" },
+                                                              { "device": 
"ide-hd1",
+                                                                
"snapshot-file":
+                                                                
"/some/place/my-image2",
+                                                                "format": 
"qcow2" } }
+<- { "return": {} }
+
+EQMP
 
     {
         .name       = "blockdev-snapshot-sync",
-- 
1.7.9.rc2.1.g69204




reply via email to

[Prev in Thread] Current Thread [Next in Thread]