* [RFC PATCH 0/7] domain snapshot implementation
@ 2015-08-11 10:44 Chunyan Liu
  2015-08-11 10:44 ` [RFC PATCH 1/7] add definitions for vm snapshot Chunyan Liu
                   ` (7 more replies)
  0 siblings, 8 replies; 9+ messages in thread
From: Chunyan Liu @ 2015-08-11 10:44 UTC (permalink / raw)
  To: xen-devel; +Cc: wei.liu2, jfehlig, Ian.Jackson, ian.campbell, Chunyan Liu
Add vm snapshot implementation. Support snapshot-create and
snapshot-revert.
Current Limitations:
About disk snapshot create, there are many cases:
 - qdisk, internal, should calls qmp command to do the work.
 - qdisk, external, should calls qmp command to do the work, qemu
   will replace disk backend file after creating external snapshot.
 - nonqdisk, internal, should call 'qemu-img snapshot' to do the work.
 - nonqdisk, external, should call 'qemu-img create' to create a new file
   with the original disk file as backing file. And libxl should replace
   domain disk from original disk to the new file.
 To the last case, during domain snapshot, between domain suspend and
 resume, how to replace the disk backend file from libxl? Especially if
 disk file format is changed (original disk backend file is 'raw', new file
 is 'qcow2')?
 Considering this, currently I exclude the non-qdisk cases, let the API
 support qdisk only. About the non-qdisk and external case, any suggestion?
About disk snapshot revert:
 Reverting from external disk snapshot is actually starting domain from a
 specified backing file, since backing file should be kept read-only, that
 will involve block copy operation. Currently this case is not supported.
 Only support reverting from internal disk snapshot.
Design document:
Latest design document is just posted.
Chunyan Liu (7):
  libxl_types.idl: add definitions for vm snapshot
  qmp: add qmp handlers to create disk snapshots
  libxl: save disk format to xenstore
  libxl: add snapshot APIs
  xl: add domain snapshot commands
  qmp: add qmp handlers to delete internal/external disk snapshot
  libxl: add APIs to delete internal/external disk snapshots
 Config.mk                            |   2 +-
 config/Paths.mk.in                   |   1 +
 configure                            |   3 +
 docs/man/xl.snapshot.conf.pod.5      |  59 +++
 m4/paths.m4                          |   3 +
 tools/configure                      |   3 +
 tools/examples/snapshot.cfg.external |   4 +
 tools/examples/snapshot.cfg.internal |   4 +
 tools/libxl/Makefile                 |   2 +
 tools/libxl/libxl.c                  |  10 +-
 tools/libxl/libxl.h                  |  51 +++
 tools/libxl/libxl_internal.h         |  38 ++
 tools/libxl/libxl_qmp.c              | 224 ++++++++++++
 tools/libxl/libxl_snapshot.c         | 321 +++++++++++++++++
 tools/libxl/libxl_types.idl          |  31 ++
 tools/libxl/libxl_types_internal.idl |   8 +
 tools/libxl/libxl_utils.c            |  16 +
 tools/libxl/libxl_utils.h            |   1 +
 tools/libxl/xl.h                     |   2 +
 tools/libxl/xl_cmdimpl.c             | 677 +++++++++++++++++++++++++++++++++++
 tools/libxl/xl_cmdtable.c            |  16 +
 21 files changed, 1474 insertions(+), 2 deletions(-)
 create mode 100644 docs/man/xl.snapshot.conf.pod.5
 create mode 100644 tools/examples/snapshot.cfg.external
 create mode 100644 tools/examples/snapshot.cfg.internal
 create mode 100644 tools/libxl/libxl_snapshot.c
-- 
2.1.4
^ permalink raw reply	[flat|nested] 9+ messages in thread
* [RFC PATCH 1/7] add definitions for vm snapshot
  2015-08-11 10:44 [RFC PATCH 0/7] domain snapshot implementation Chunyan Liu
@ 2015-08-11 10:44 ` Chunyan Liu
  2015-08-11 10:44 ` [RFC PATCH 2/7] qmp: add qmp handlers to create disk snapshots Chunyan Liu
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Chunyan Liu @ 2015-08-11 10:44 UTC (permalink / raw)
  To: xen-devel; +Cc: wei.liu2, jfehlig, Ian.Jackson, ian.campbell, Chunyan Liu
Define libxl_disk_snapshot_type and libxl_disk_snapshot for VM
snapshot usage.
Signed-off-by: Chunyan Liu <cyliu@suse.com>
---
 tools/libxl/libxl_types.idl          | 31 +++++++++++++++++++++++++++++++
 tools/libxl/libxl_types_internal.idl |  8 ++++++++
 2 files changed, 39 insertions(+)
diff --git a/tools/libxl/libxl_types.idl b/tools/libxl/libxl_types.idl
index ef346e7..f7a4c3e 100644
--- a/tools/libxl/libxl_types.idl
+++ b/tools/libxl/libxl_types.idl
@@ -793,3 +793,34 @@ libxl_psr_cat_info = Struct("psr_cat_info", [
     ("cos_max", uint32),
     ("cbm_len", uint32),
     ])
+
+libxl_disk_snapshot_type = Enumeration("disk_snapshot_type", [
+    (0, "INVALID"),
+    (1, "INTERNAL"),
+    (2, "EXTERNAL"),
+    ])
+
+libxl_disk_snapshot = Struct("disk_snapshot",[
+    # target disk
+    ("disk", libxl_device_disk),
+
+    # disk snapshot name
+    ("name", string),
+
+    ("u", KeyedUnion(None, libxl_disk_snapshot_type, "type",
+         [("external", Struct(None, [
+
+            # disk format for external files. Since external disk snapshot is
+            # implemented with backing file mechanism, the external file disk
+            # format must support backing file. This field can be NULL, then
+            # a proper disk format will be used by default according to the
+            # orignal disk format.
+            ("external_format", libxl_disk_format),
+
+            # external file path. This field should be non-NULL and a new path.
+            ("external_path",   string),
+            ])),
+          ("internal", None),
+          ("invalid", None),
+         ])),
+    ])
diff --git a/tools/libxl/libxl_types_internal.idl b/tools/libxl/libxl_types_internal.idl
index 5e55685..60dce1d 100644
--- a/tools/libxl/libxl_types_internal.idl
+++ b/tools/libxl/libxl_types_internal.idl
@@ -45,3 +45,11 @@ libxl__device_action = Enumeration("device_action", [
     (1, "ADD"),
     (2, "REMOVE"),
     ])
+
+libxl_disk_snapshot_op = Enumeration("disk_snapshot_op", [
+    (1, "CREATE"),
+    (2, "DELETE"),
+    (3, "REVERT"),
+    (4, "LIST"),
+    ])
+
-- 
2.1.4
^ permalink raw reply related	[flat|nested] 9+ messages in thread
* [RFC PATCH 2/7] qmp: add qmp handlers to create disk snapshots
  2015-08-11 10:44 [RFC PATCH 0/7] domain snapshot implementation Chunyan Liu
  2015-08-11 10:44 ` [RFC PATCH 1/7] add definitions for vm snapshot Chunyan Liu
@ 2015-08-11 10:44 ` Chunyan Liu
  2015-08-11 10:44 ` [RFC PATCH 3/7] libxl: save disk format to xenstore Chunyan Liu
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Chunyan Liu @ 2015-08-11 10:44 UTC (permalink / raw)
  To: xen-devel; +Cc: wei.liu2, jfehlig, Ian.Jackson, ian.campbell, Chunyan Liu
Add qmp handlers to take disk snapshots. This will be used when
creating a domain snapshots.
Signed-off-by: Chunyan Liu <cyliu@suse.com>
---
 tools/libxl/libxl_internal.h |  4 +++
 tools/libxl/libxl_qmp.c      | 66 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 70 insertions(+)
diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h
index 6ea6c83..c3dec85 100644
--- a/tools/libxl/libxl_internal.h
+++ b/tools/libxl/libxl_internal.h
@@ -1735,6 +1735,10 @@ _hidden int libxl__qmp_set_global_dirty_log(libxl__gc *gc, int domid, bool enabl
 _hidden int libxl__qmp_insert_cdrom(libxl__gc *gc, int domid, const libxl_device_disk *disk);
 /* Add a virtual CPU */
 _hidden int libxl__qmp_cpu_add(libxl__gc *gc, int domid, int index);
+/* Create disk snapshots for a group of disks in a transaction */
+_hidden int libxl__qmp_disk_snapshot_transaction(libxl__gc *gc, int domid,
+                                                libxl_disk_snapshot *snapshot,
+                                                int nb);
 /* close and free the QMP handler */
 _hidden void libxl__qmp_close(libxl__qmp_handler *qmp);
 /* remove the socket file, if the file has already been removed,
diff --git a/tools/libxl/libxl_qmp.c b/tools/libxl/libxl_qmp.c
index 965c507..2216511 100644
--- a/tools/libxl/libxl_qmp.c
+++ b/tools/libxl/libxl_qmp.c
@@ -968,6 +968,72 @@ int libxl__qmp_cpu_add(libxl__gc *gc, int domid, int idx)
     return qmp_run_command(gc, domid, "cpu-add", args, NULL, NULL);
 }
 
+/*
+ * requires QEMU version >= 1.1
+ * qmp command example:
+ * -> { "execute": "transaction",
+ *      "arguments": { "actions": [
+ *          { "type": "blockdev-snapshot-sync", "data" : { "device": "ide-hd0",
+ *                                          "snapshot-file": "/some/place/my-image",
+ *                                          "format": "qcow2" } },
+ *          { 'type': 'blockdev-snapshot-internal-sync', 'data' : {
+ *                                          "device": "ide-hd1",
+ *                                          "name": "snapshot0" } } ] } }
+ *          { 'type': 'blockdev-snapshot-internal-sync', 'data' : {
+ *                                          "device": "ide-hd2",
+ *                                          "name": "snapshot0" } } ] } }
+ * <- { "return": {} }
+ */
+int libxl__qmp_disk_snapshot_transaction(libxl__gc *gc, int domid,
+                                         libxl_disk_snapshot *snapshot,
+                                         int nb)
+{
+    libxl__json_object *args = NULL;
+    libxl__json_object *actions = NULL;
+    libxl__json_object **type = NULL;
+    libxl__json_object **data = NULL;
+    int i;
+
+    type = (libxl__json_object**)calloc(nb, sizeof(libxl__json_object*));
+    data = (libxl__json_object**)calloc(nb, sizeof(libxl__json_object*));
+    actions = libxl__json_object_alloc(gc, JSON_ARRAY);
+
+    for (i = 0; i < nb; i++) {
+        switch (snapshot[i].type) {
+        case LIBXL_DISK_SNAPSHOT_TYPE_INTERNAL:
+            /* internal disk snapshot */
+            qmp_parameters_add_string(gc, &type[i], "type",
+                                      "blockdev-snapshot-internal-sync");
+            qmp_parameters_add_string(gc, &data[i], "name",
+                                      snapshot[i].name);
+            qmp_parameters_add_string(gc, &data[i], "device",
+                                      snapshot[i].disk.vdev);
+            qmp_parameters_common_add(gc, &type[i], "data", data[i]);
+            flexarray_append(actions->u.array, (void*)type[i]);
+            break;
+        case LIBXL_DISK_SNAPSHOT_TYPE_EXTERNAL:
+            /* external disk snapshot */
+            qmp_parameters_add_string(gc, &type[i], "type",
+                                      "blockdev-snapshot-sync");
+            qmp_parameters_add_string(gc, &data[i], "device",
+                                      snapshot[i].disk.vdev);
+            qmp_parameters_add_string(gc, &data[i], "snapshot-file",
+                                      snapshot[i].u.external.external_path);
+            qmp_parameters_add_string(gc, &data[i], "format",
+                libxl_disk_format_to_string(snapshot[i].u.external.external_format));
+            qmp_parameters_common_add(gc, &type[i], "data", data[i]);
+            flexarray_append(actions->u.array, (void*)type[i]);
+            break;
+        default:
+            LOG(ERROR, "Invalid disk snapshot type");
+            return -1;
+        }
+    }
+
+    qmp_parameters_common_add(gc, &args, "actions", actions);
+    return qmp_run_command(gc, domid, "transaction", args, NULL, NULL);
+}
+
 int libxl__qmp_initializations(libxl__gc *gc, uint32_t domid,
                                const libxl_domain_config *guest_config)
 {
-- 
2.1.4
^ permalink raw reply related	[flat|nested] 9+ messages in thread
* [RFC PATCH 3/7] libxl: save disk format to xenstore
  2015-08-11 10:44 [RFC PATCH 0/7] domain snapshot implementation Chunyan Liu
  2015-08-11 10:44 ` [RFC PATCH 1/7] add definitions for vm snapshot Chunyan Liu
  2015-08-11 10:44 ` [RFC PATCH 2/7] qmp: add qmp handlers to create disk snapshots Chunyan Liu
@ 2015-08-11 10:44 ` Chunyan Liu
  2015-08-11 10:44 ` [RFC PATCH 4/7] libxl: add snapshot APIs Chunyan Liu
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Chunyan Liu @ 2015-08-11 10:44 UTC (permalink / raw)
  To: xen-devel; +Cc: wei.liu2, jfehlig, Ian.Jackson, ian.campbell, Chunyan Liu
Disk snapshot handling depends on disk format. Currently since disk
format is not saved into xenstore, when getting device disk list,
disk->format is LIBXL_DISK_FORMAT_UNKNOWN. Disk snapshot cannot
continue without correct disk format information, so adding code
to save disk format to xenstore so that when getting device disk
list, disk->format contains correct information.
Signed-off-by: Chunyan Liu <cyliu@suse.com>
---
 tools/libxl/libxl.c       | 10 +++++++++-
 tools/libxl/libxl_utils.c | 16 ++++++++++++++++
 tools/libxl/libxl_utils.h |  1 +
 3 files changed, 26 insertions(+), 1 deletion(-)
diff --git a/tools/libxl/libxl.c b/tools/libxl/libxl.c
index 083f099..dce43d6 100644
--- a/tools/libxl/libxl.c
+++ b/tools/libxl/libxl.c
@@ -2524,6 +2524,8 @@ static void device_disk_add(libxl__egc *egc, uint32_t domid,
                 goto out;
         }
 
+        flexarray_append(back, "format");
+        flexarray_append(back, libxl__device_disk_string_of_format(disk->format));
         flexarray_append(back, "frontend-id");
         flexarray_append(back, libxl__sprintf(gc, "%d", domid));
         flexarray_append(back, "online");
@@ -2682,7 +2684,13 @@ static int libxl__device_disk_from_xs_be(libxl__gc *gc,
     }
     disk->is_cdrom = !strcmp(tmp, "cdrom");
 
-    disk->format = LIBXL_DISK_FORMAT_UNKNOWN;
+    tmp = libxl__xs_read(gc, XBT_NULL,
+                         libxl__sprintf(gc, "%s/format", be_path));
+    if (!tmp) {
+        LOG(ERROR, "Missing xenstore node %s/format", be_path);
+        goto cleanup;
+    }
+    libxl_string_to_format(ctx, tmp, &(disk->format));
 
     return 0;
 cleanup:
diff --git a/tools/libxl/libxl_utils.c b/tools/libxl/libxl_utils.c
index bfc9699..067a9fc 100644
--- a/tools/libxl/libxl_utils.c
+++ b/tools/libxl/libxl_utils.c
@@ -322,6 +322,22 @@ out:
     return rc;
 }
 
+int libxl_string_to_format(libxl_ctx *ctx, char *s, libxl_disk_format *format)
+{
+    int rc = 0;
+
+    if (!strcmp(s, "aio")) {
+        *format = LIBXL_DISK_FORMAT_RAW;
+    } else if (!strcmp(s, "vhd")) {
+        *format = LIBXL_DISK_FORMAT_VHD;
+    } else if (!strcmp(s, "qcow")) {
+        *format = LIBXL_DISK_FORMAT_QCOW;
+    } else if (!strcmp(s, "qcow2")) {
+        *format = LIBXL_DISK_FORMAT_QCOW2;
+    }
+    return rc;
+}
+
 int libxl_read_file_contents(libxl_ctx *ctx, const char *filename,
                              void **data_r, int *datalen_r) {
     GC_INIT(ctx);
diff --git a/tools/libxl/libxl_utils.h b/tools/libxl/libxl_utils.h
index 1e5ca8a..0897069 100644
--- a/tools/libxl/libxl_utils.h
+++ b/tools/libxl/libxl_utils.h
@@ -37,6 +37,7 @@ int libxl_get_stubdom_id(libxl_ctx *ctx, int guest_domid);
 int libxl_is_stubdom(libxl_ctx *ctx, uint32_t domid, uint32_t *target_domid);
 int libxl_create_logfile(libxl_ctx *ctx, const char *name, char **full_name);
 int libxl_string_to_backend(libxl_ctx *ctx, char *s, libxl_disk_backend *backend);
+int libxl_string_to_format(libxl_ctx *ctx, char *s, libxl_disk_format *format);
 
 int libxl_read_file_contents(libxl_ctx *ctx, const char *filename,
                              void **data_r, int *datalen_r);
-- 
2.1.4
^ permalink raw reply related	[flat|nested] 9+ messages in thread
* [RFC PATCH 4/7] libxl: add snapshot APIs
  2015-08-11 10:44 [RFC PATCH 0/7] domain snapshot implementation Chunyan Liu
                   ` (2 preceding siblings ...)
  2015-08-11 10:44 ` [RFC PATCH 3/7] libxl: save disk format to xenstore Chunyan Liu
@ 2015-08-11 10:44 ` Chunyan Liu
  2015-08-11 10:44 ` [RFC PATCH 5/7] xl: add domain snapshot commands Chunyan Liu
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Chunyan Liu @ 2015-08-11 10:44 UTC (permalink / raw)
  To: xen-devel; +Cc: wei.liu2, jfehlig, Ian.Jackson, ian.campbell, Chunyan Liu
Add snapshot related APIs for xl, including:
create disk snapshots, revert disk snapshots.
Together with existing memory save/restore APIs, xl can create domain
snapshot and revert from a domain snapshot.
Limitations:
About disk snapshot create, there are many cases:
- qdisk, internal, should calls qmp command to do the work.
- qdisk, external, should calls qmp command to do the work, qemu
  will replace disk backend file after creating external snapshot.
- nonqdisk, internal, should call 'qemu-img snapshot' to do the work.
- nonqdisk, external, should call 'qemu-img create' to create a new file
  with the original disk file as backing file. And libxl should replace
  domain disk from original disk to the new file.
Problem is: to the last case, during domain snapshot, between domain suspend
and resume, how to replace the disk backend file from libxl? Especially if
disk file format is changed (original disk backend file is 'raw', new file
is 'qcow2') ?
Considering this, currently the API only support qdisk, non-qdisk cases
are not included.
About disk snapshot revert:
Reverting from external disk snapshot is actually starting domain from a
specified backing file, since backing file should be kept read-only, that
will involve block copy operation. Currently this case is not supported.
Only support reverting from internal disk snapshot.
Signed-off-by: Chunyan Liu <cyliu@suse.com>
---
 tools/libxl/Makefile         |   1 +
 tools/libxl/libxl.h          |   6 ++
 tools/libxl/libxl_internal.h |   6 ++
 tools/libxl/libxl_snapshot.c | 219 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 232 insertions(+)
 create mode 100644 tools/libxl/libxl_snapshot.c
diff --git a/tools/libxl/Makefile b/tools/libxl/Makefile
index 9036076..0917326 100644
--- a/tools/libxl/Makefile
+++ b/tools/libxl/Makefile
@@ -105,6 +105,7 @@ LIBXL_OBJS = flexarray.o libxl.o libxl_create.o libxl_dm.o libxl_pci.o \
 			libxl_qmp.o libxl_event.o libxl_fork.o \
 			libxl_dom_suspend.o $(LIBXL_OBJS-y)
 LIBXL_OBJS += libxl_genid.o
+LIBXL_OBJS += libxl_snapshot.o
 LIBXL_OBJS += _libxl_types.o libxl_flask.o _libxl_types_internal.o
 
 LIBXL_TESTS += timedereg
diff --git a/tools/libxl/libxl.h b/tools/libxl/libxl.h
index 5f9047c..d60f139 100644
--- a/tools/libxl/libxl.h
+++ b/tools/libxl/libxl.h
@@ -1729,6 +1729,12 @@ int libxl_psr_cat_get_l3_info(libxl_ctx *ctx, libxl_psr_cat_info **info,
 void libxl_psr_cat_info_list_free(libxl_psr_cat_info *list, int nr);
 #endif
 
+/* Domain snapshot related APIs */
+int libxl_disk_snapshot_create(libxl_ctx *ctx, uint32_t domid,
+                               libxl_disk_snapshot *snapshot, int nb);
+int libxl_disk_snapshot_revert(libxl_ctx *ctx, uint32_t domid,
+                               libxl_disk_snapshot *snapshot, int nb);
+
 /* misc */
 
 /* Each of these sets or clears the flag according to whether the
diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h
index c3dec85..f24e0af 100644
--- a/tools/libxl/libxl_internal.h
+++ b/tools/libxl/libxl_internal.h
@@ -1749,6 +1749,12 @@ _hidden void libxl__qmp_cleanup(libxl__gc *gc, uint32_t domid);
 _hidden int libxl__qmp_initializations(libxl__gc *gc, uint32_t domid,
                                        const libxl_domain_config *guest_config);
 
+typedef struct libxl__ao_snapshot libxl__ao_snapshot;
+struct libxl__ao_snapshot {
+    libxl__ao *ao;
+    libxl__ev_child child;
+};
+
 /* on failure, logs */
 int libxl__sendmsg_fds(libxl__gc *gc, int carrier,
                        const void *data, size_t datalen,
diff --git a/tools/libxl/libxl_snapshot.c b/tools/libxl/libxl_snapshot.c
new file mode 100644
index 0000000..34d36ef
--- /dev/null
+++ b/tools/libxl/libxl_snapshot.c
@@ -0,0 +1,219 @@
+/*
+ * libxl_snapshot.c: code domain snapshot related APIs
+ *
+ * Copyright (C) 2015 SUSE LINUX Products GmbH, Nuernberg, Germany.
+ * Author Chunyan Liu <cyliu@suse.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * 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 Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+/* Replace domain disk to external path after taking external disk
+ * snapshot, since original disk becomes backing file. It will need
+ * to update xenstore information as well as domain config.
+ */
+static int libxl__update_disk_configuration(libxl__gc *gc, uint32_t domid,
+                                            libxl_disk_snapshot snapshot)
+{
+    char *backend_path, *path, *value;
+    const char *format;
+    int i, rc;
+    libxl__device device;
+    libxl_domain_config d_config;
+    libxl__domain_userdata_lock *lock = NULL;
+
+    /* update domain config */
+    libxl_domain_config_init(&d_config);
+    lock = libxl__lock_domain_userdata(gc, domid);
+    if (!lock) {
+        rc = ERROR_LOCK_FAIL;
+        goto out;
+    }
+
+    rc = libxl__get_domain_configuration(gc, domid, &d_config);
+    if (rc) goto out;
+
+    for (i = 0; i < d_config.num_disks; i++) {
+        if (!strcmp(d_config.disks[i].vdev, snapshot.disk.vdev)) {
+            free(d_config.disks[i].pdev_path);
+            d_config.disks[i].pdev_path =
+                strdup(snapshot.u.external.external_path);
+            d_config.disks[i].format =
+                snapshot.u.external.external_format;
+        }
+    }
+
+    rc = libxl__set_domain_configuration(gc, domid, &d_config);
+    if (rc) goto out;
+
+    /* update xenstore */
+    rc = libxl__device_from_disk(gc, domid, &snapshot.disk, &device);
+    if (rc) {
+        LOG(ERROR, "Update xenstore disk information failed");
+        goto out;
+    }
+    backend_path = libxl__device_backend_path(gc, &device);
+
+    path = GCSPRINTF("%s/params", backend_path);
+    format =
+        libxl_disk_format_to_string(snapshot.u.external.external_format);
+    value = GCSPRINTF("%s:%s", format,
+                      snapshot.u.external.external_path);
+
+    rc = libxl__xs_write_checked(gc, XBT_NULL, path, value);
+    if (rc) {
+        LOG(ERROR, "Update xenstore disk information failed");
+        goto out;
+    }
+
+    path = GCSPRINTF("%s/format", backend_path);
+    rc = libxl__xs_write_checked(gc, XBT_NULL, path, format);
+    if (rc) {
+        LOG(ERROR, "Update xenstore disk information failed");
+        goto out;
+    }
+
+    rc = 0;
+
+out:
+    if (lock) libxl__unlock_domain_userdata(lock);
+    libxl_domain_config_dispose(&d_config);
+    return rc;
+}
+
+int libxl_disk_snapshot_create(libxl_ctx *ctx, uint32_t domid,
+                               libxl_disk_snapshot *snapshot, int nb)
+{
+    int i;
+    GC_INIT(ctx);
+    int rc;
+
+    rc = libxl__qmp_disk_snapshot_transaction(gc, domid, snapshot, nb);
+    if (rc) {
+        LOG(ERROR, "domain disk snapshot create fail\n");
+        goto out;
+    }
+
+    for (i = 0; i < nb; i++) {
+        if (snapshot[i].type == LIBXL_DISK_SNAPSHOT_TYPE_EXTERNAL) {
+            /* Domain disk points to the new disk, original disk becomes
+            * backing file. Now, we need to update xenstore information
+            * and domain config.
+            */
+            rc = libxl__update_disk_configuration(gc, domid, snapshot[i]);
+            if (rc) goto out;
+        }
+    }
+
+out:
+    GC_FREE;
+    return rc;
+}
+
+static void snapshot_finished(libxl__egc *egc, libxl__ev_child *child,
+                              pid_t pid, int status)
+{
+    libxl__ao_snapshot *ao_snapshot = CONTAINER_OF(child, libxl__ao_snapshot, child);
+    STATE_AO_GC(ao_snapshot->ao);
+    int rc = 0;
+
+    if (status) {
+        LOG(ERROR, "cmd failed");
+        libxl_report_child_exitstatus(CTX, XTL_ERROR, "cmd",
+                                      pid, status);
+        rc = ERROR_FAIL;
+        goto out;
+    } else {
+        LOG(DEBUG, "cmd completed");
+    }
+
+out:
+    libxl__ao_complete(egc, ao, rc);
+    return;
+}
+
+static int internal_disk_snapshot_helper(libxl_ctx *ctx, uint32_t domid,
+                                         libxl_disk_snapshot *snapshot,
+                                         libxl__disk_snapshot_op op)
+{
+    AO_CREATE(ctx, domid, NULL);
+    libxl__ao_snapshot *ao_snapshot;
+    const char **args;
+    int i, rc;
+
+    GCNEW(ao_snapshot);
+    ao_snapshot->ao = ao;
+
+    switch (snapshot->disk.format) {
+    case LIBXL_DISK_FORMAT_QCOW2:
+        i = 0;
+        args = libxl__zalloc(gc, 6 * sizeof(*args));
+        args[i++] = LIBEXEC_BIN "/" "qemu-img";
+        args[i++] = "snapshot";
+        switch (op) {
+        case LIBXL__DISK_SNAPSHOT_OP_REVERT:
+            args[i++] = "-a";
+            break;
+        case LIBXL__DISK_SNAPSHOT_OP_CREATE:
+            args[i++] = "-c";
+            break;
+        case LIBXL__DISK_SNAPSHOT_OP_DELETE:
+            args[i++] = "-d";
+            break;
+        default:
+            LOG(ERROR, "disk snapshot helper: unsupported operation");
+            rc = -1;
+            goto out;
+        }
+        args[i++] = GCSPRINTF("%s", snapshot->name);
+        args[i++] = GCSPRINTF("%s", snapshot->disk.pdev_path);
+        break;
+    default:
+        LOG(ERROR, "disk snapshot helper: unsupported disk format");
+        rc = -1;
+        goto out;
+    }
+
+    pid_t pid = libxl__ev_child_fork(gc, &ao_snapshot->child, snapshot_finished);
+    if (pid == -1) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    if (!pid) {
+        /* child */
+        libxl__exec(gc, -1, -1, -1, args[0], (char **)args, NULL);
+        exit(-1);
+    }
+
+    /* parent */
+    return AO_INPROGRESS;
+
+out:
+    return rc;
+}
+
+int libxl_disk_snapshot_revert(libxl_ctx *ctx, uint32_t domid,
+                               libxl_disk_snapshot *snapshot, int nb)
+{
+    int i;
+    int rc = 0;
+
+    for ( i = 0; i < nb; i++ ) {
+        assert(snapshot[i].type == LIBXL_DISK_SNAPSHOT_TYPE_INTERNAL);
+        rc = internal_disk_snapshot_helper(ctx, domid, &snapshot[i],
+                                           LIBXL__DISK_SNAPSHOT_OP_REVERT);
+    }
+    return rc;
+}
-- 
2.1.4
^ permalink raw reply related	[flat|nested] 9+ messages in thread
* [RFC PATCH 5/7] xl: add domain snapshot commands
  2015-08-11 10:44 [RFC PATCH 0/7] domain snapshot implementation Chunyan Liu
                   ` (3 preceding siblings ...)
  2015-08-11 10:44 ` [RFC PATCH 4/7] libxl: add snapshot APIs Chunyan Liu
@ 2015-08-11 10:44 ` Chunyan Liu
  2015-08-11 10:44 ` [RFC PATCH 6/7] qmp: add qmp handlers to delete internal/external disk snapshot Chunyan Liu
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Chunyan Liu @ 2015-08-11 10:44 UTC (permalink / raw)
  To: xen-devel; +Cc: wei.liu2, jfehlig, Ian.Jackson, ian.campbell, Chunyan Liu
Add domain snapshot create/revert commands implementation.
Since xl is expected to not maintain domain snapshot info itself,
it has no idea about how many snapshots and snapshot related
files and info, xl won't supply snapshot delete command. It'll
depend on users to delete things.
Signed-off-by: Chunyan Liu <cyliu@suse.com>
---
 Config.mk                            |   2 +-
 config/Paths.mk.in                   |   1 +
 configure                            |   3 +
 docs/man/xl.snapshot.conf.pod.5      |  59 +++
 m4/paths.m4                          |   3 +
 tools/configure                      |   3 +
 tools/examples/snapshot.cfg.external |   4 +
 tools/examples/snapshot.cfg.internal |   4 +
 tools/libxl/Makefile                 |   1 +
 tools/libxl/xl.h                     |   2 +
 tools/libxl/xl_cmdimpl.c             | 677 +++++++++++++++++++++++++++++++++++
 tools/libxl/xl_cmdtable.c            |  16 +
 12 files changed, 774 insertions(+), 1 deletion(-)
 create mode 100644 docs/man/xl.snapshot.conf.pod.5
 create mode 100644 tools/examples/snapshot.cfg.external
 create mode 100644 tools/examples/snapshot.cfg.internal
diff --git a/Config.mk b/Config.mk
index e9a7097..aa4884f 100644
--- a/Config.mk
+++ b/Config.mk
@@ -159,7 +159,7 @@ endef
 
 BUILD_MAKE_VARS := sbindir bindir LIBEXEC LIBEXEC_BIN libdir SHAREDIR \
                    XENFIRMWAREDIR XEN_CONFIG_DIR XEN_SCRIPT_DIR XEN_LOCK_DIR \
-                   XEN_RUN_DIR XEN_PAGING_DIR XEN_DUMP_DIR
+                   XEN_RUN_DIR XEN_PAGING_DIR XEN_DUMP_DIR XEN_SNAPSHOT_DIR
 
 buildmakevars2file = $(eval $(call buildmakevars2file-closure,$(1)))
 define buildmakevars2file-closure
diff --git a/config/Paths.mk.in b/config/Paths.mk.in
index d36504f..8e7d2a8 100644
--- a/config/Paths.mk.in
+++ b/config/Paths.mk.in
@@ -49,6 +49,7 @@ BASH_COMPLETION_DIR      := $(CONFIG_DIR)/bash_completion.d
 XEN_LOCK_DIR             := @XEN_LOCK_DIR@
 XEN_PAGING_DIR           := @XEN_PAGING_DIR@
 XEN_DUMP_DIR             := @XEN_DUMP_DIR@
+XEN_SNAPSHOT_DIR         := @XEN_SNAPSHOT_DIR@
 
 XENFIRMWAREDIR           := @XENFIRMWAREDIR@
 
diff --git a/configure b/configure
index 80b27d6..e283d17 100755
--- a/configure
+++ b/configure
@@ -595,6 +595,7 @@ tools
 xen
 subdirs
 XEN_DUMP_DIR
+XEN_SNAPSHOT_DIR
 XEN_PAGING_DIR
 XEN_LOCK_DIR
 XEN_SCRIPT_DIR
@@ -1984,6 +1985,8 @@ XEN_PAGING_DIR=$localstatedir/lib/xen/xenpaging
 XEN_DUMP_DIR=$xen_dumpdir_path
 
 
+XEN_SNAPSHOT_DIR=$localstatedir/lib/xen/snapshot
+
 
 case "$host_cpu" in
     i[3456]86|x86_64)
diff --git a/docs/man/xl.snapshot.conf.pod.5 b/docs/man/xl.snapshot.conf.pod.5
new file mode 100644
index 0000000..28c2196
--- /dev/null
+++ b/docs/man/xl.snapshot.conf.pod.5
@@ -0,0 +1,59 @@
+=head1 NAME
+
+xl.snapshot.cfg - XL Domain Snapshot Configuration File Syntax
+
+=head1 DESCRIPTION
+
+Snapshot configuration file will be used in xl snapshot-create
+and xl snapshot-revert.
+
+Without snapshot configuration file, xl snapshot-create could create
+domain snapshot with default value. To create a user-defined domain
+snapshot, xl requires a domain snapshot config file.
+
+For snapshot-revert, it's mandatory, each item should be specified.
+
+Two examples for internal domain snapshot and external domain snapshot
+could be found in:
+"/etc/xen/examples/snapshot.cfg.internal"
+"/etc/xen/examples/snapshot.cfg.external"
+
+=head1 SYNTAX
+
+A domain config file consists of a series of C<KEY=VALUE> pairs. It
+shares the same rules with xl.cfg
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<name="NAME">
+
+Specifies the name of the domain snapshot. If ignored, it will be the
+epoch second from 1, Jan 1970. It will be used for taking internal
+disk snapshot, generate memory state file name and generate external
+disk snapshot file name.
+
+=item B<memory="0|1">
+
+Indicates whether to save memory state file. If not, it will take a
+disk-only snapshot. Currently xl doesn't support disk-only snapshot,
+so it can only be '1'.
+
+=item B<memory_path="PATHNAME">
+
+Location of memory state file. This state file is as same as the file
+in "xl save". The value is the full directory of the location of memory
+state file. If ignored, it will be generated by default:
+<snapshot path>/<snapshot name>.save
+
+=item B<disks=[ "DISK_SPEC_STRING", "DISK_SPEC_STRING", ...]>
+
+Disk snapshot description.
+DISK_SPEC_STRING syntax is:
+'external path, external format, target device'
+If taking a internal disk snapshot, keep 'external path' and
+'external format' to be '', e.g. [',,xvda',].
+
+=back
+
diff --git a/m4/paths.m4 b/m4/paths.m4
index 63e0f6b..abd89d2 100644
--- a/m4/paths.m4
+++ b/m4/paths.m4
@@ -122,4 +122,7 @@ AC_SUBST(XEN_PAGING_DIR)
 
 XEN_DUMP_DIR=$xen_dumpdir_path
 AC_SUBST(XEN_DUMP_DIR)
+
+XEN_SNAPSHOT_DIR=$localstatedir/lib/xen/snapshot
+AC_SUBST(XEN_SNAPSHOT_DIR)
 ])
diff --git a/tools/configure b/tools/configure
index 1098f1f..cd604bb 100755
--- a/tools/configure
+++ b/tools/configure
@@ -716,6 +716,7 @@ monitors
 githttp
 rpath
 XEN_DUMP_DIR
+XEN_SNAPSHOT_DIR
 XEN_PAGING_DIR
 XEN_LOCK_DIR
 XEN_SCRIPT_DIR
@@ -3950,6 +3951,8 @@ XEN_PAGING_DIR=$localstatedir/lib/xen/xenpaging
 XEN_DUMP_DIR=$xen_dumpdir_path
 
 
+XEN_SNAPSHOT_DIR=$localstatedir/lib/xen/snapshot
+
 
 # Enable/disable options
 
diff --git a/tools/examples/snapshot.cfg.external b/tools/examples/snapshot.cfg.external
new file mode 100644
index 0000000..d01aaa0
--- /dev/null
+++ b/tools/examples/snapshot.cfg.external
@@ -0,0 +1,4 @@
+name="1439280121"
+memory=1
+memory_path="/var/lib/xen/snapshot/646d7709-7974-3e8b-03c0-e415ae619b97/1439280121/1439280121.save"
+disks=['/var/lib/xen/snapshot/646d7709-7974-3e8b-03c0-e415ae619b97/1439280121/xvda_1439280121.qcow2,qcow2,xvda',]
diff --git a/tools/examples/snapshot.cfg.internal b/tools/examples/snapshot.cfg.internal
new file mode 100644
index 0000000..9eeef0f
--- /dev/null
+++ b/tools/examples/snapshot.cfg.internal
@@ -0,0 +1,4 @@
+name="1439280559"
+memory=1
+memory_path="/var/lib/xen/snapshot/646d7709-7974-3e8b-03c0-e415ae619b97/1439280559/1439280559.save"
+disks=[',,xvda']
diff --git a/tools/libxl/Makefile b/tools/libxl/Makefile
index 0917326..ea38d35 100644
--- a/tools/libxl/Makefile
+++ b/tools/libxl/Makefile
@@ -280,6 +280,7 @@ install: all
 	$(INSTALL_DIR) $(DESTDIR)$(BASH_COMPLETION_DIR)
 	$(INSTALL_DIR) $(DESTDIR)$(LIBEXEC_BIN)
 	$(INSTALL_DIR) $(DESTDIR)$(SHAREDIR)/pkgconfig
+	$(INSTALL_DIR) -m 0700 $(DESTDIR)$(XEN_SNAPSHOT_DIR)
 	$(INSTALL_PROG) xl $(DESTDIR)$(sbindir)
 	$(INSTALL_PROG) xen-init-dom0 $(DESTDIR)$(LIBEXEC_BIN)
 	$(INSTALL_PROG) libxl-save-helper $(DESTDIR)$(LIBEXEC_BIN)
diff --git a/tools/libxl/xl.h b/tools/libxl/xl.h
index 13bccba..0ef5868 100644
--- a/tools/libxl/xl.h
+++ b/tools/libxl/xl.h
@@ -121,6 +121,8 @@ int main_psr_cmt_show(int argc, char **argv);
 int main_psr_cat_cbm_set(int argc, char **argv);
 int main_psr_cat_show(int argc, char **argv);
 #endif
+int main_snapshot_create(int argc, char **argv);
+int main_snapshot_revert(int argc, char **argv);
 
 void help(const char *command);
 
diff --git a/tools/libxl/xl_cmdimpl.c b/tools/libxl/xl_cmdimpl.c
index 499a05c..4f4dc64 100644
--- a/tools/libxl/xl_cmdimpl.c
+++ b/tools/libxl/xl_cmdimpl.c
@@ -160,6 +160,15 @@ struct domain_create {
     char **migration_domname_r; /* from malloc */
 };
 
+struct _domain_snapshot {
+    char *path;
+    char *name;
+    char *memory_path;
+    int ndisks;
+    libxl_disk_snapshot *disks;
+};
+
+typedef struct _domain_snapshot domain_snapshot;
 
 static uint32_t find_domain(const char *p) __attribute__((warn_unused_result));
 static uint32_t find_domain(const char *p)
@@ -8554,6 +8563,674 @@ int main_psr_hwinfo(int argc, char **argv)
 
 #endif
 
+static bool
+check_domain_snapshot_config(domain_snapshot *info, bool live)
+{
+    int i;
+
+    if (!info->memory_path) {
+        LOG("No memory save file is specified.");
+        return false;
+    }
+
+    for (i = 0; i < info->ndisks; i++) {
+        libxl_disk_snapshot disk_snap;
+        disk_snap = info->disks[i];
+
+        /* Current limitation:
+         * 1. only support 'raw' and 'qcow2', qdisk backend.
+         * 2. For 'raw', only support external disk snapshot.
+         */
+        if (disk_snap.disk.backend != LIBXL_DISK_BACKEND_QDISK) {
+            LOG("Only qdisk backend is supported currently");
+            return false;
+        }
+
+        switch (disk_snap.disk.format) {
+        case LIBXL_DISK_FORMAT_RAW:
+            if (disk_snap.type != LIBXL_DISK_SNAPSHOT_TYPE_EXTERNAL) {
+               LOG("Only support external disk snapshot for a raw disk");
+               return false;
+            }
+            break;
+        case LIBXL_DISK_FORMAT_QCOW2:
+            break;
+        default:
+            LOG("Unsupported disk image format to do disk snapshot");
+            return false;
+        }
+
+        if (disk_snap.type == LIBXL_DISK_SNAPSHOT_TYPE_EXTERNAL &&
+            !disk_snap.u.external.external_path) {
+            LOG("Error: external path is not specified in external disk snapshot.");
+            return false;
+        }
+    }
+
+    return true;
+}
+
+/* actually create a domain snapshot */
+static int
+domain_snapshot_create(uint32_t domid, domain_snapshot *info, bool live)
+{
+    uint8_t *config_data;
+    int config_len;
+    int fd, rc;
+
+    if (!check_domain_snapshot_config(info, live))
+        return -1;
+
+    /* save memory */
+    save_domain_core_begin(domid, NULL, &config_data, &config_len);
+
+    if (!config_len) {
+        fputs(" Savefile will not contain xl domain config\n", stderr);
+    }
+
+    fd = open(info->memory_path, O_WRONLY|O_CREAT|O_TRUNC, 0644);
+    if (fd < 0) {
+        fprintf(stderr, "Failed to open file %s for writing\n",
+                info->memory_path);
+        return -1;
+    }
+
+    save_domain_core_writeconfig(fd, info->memory_path,
+                                 config_data, config_len);
+
+    rc = libxl_domain_suspend(ctx, domid, fd, live, NULL);
+    close(fd);
+
+    if (rc < 0) {
+        fprintf(stderr, "Failed to save domain\n");
+        goto out;
+    }
+
+    /* take disk snapshot */
+    rc = libxl_disk_snapshot_create(ctx, domid, info->disks, info->ndisks);
+    if (rc < 0) {
+        fprintf(stderr, "Failed to create disk snapshots\n");
+        unlink(info->memory_path);
+        goto out;
+    }
+
+out:
+    libxl_domain_resume(ctx, domid, 1, 0);
+    return rc;
+}
+
+static int
+domain_snapshot_revert(uint32_t domid, domain_snapshot *snapshot, bool paused)
+{
+    int rc;
+    struct domain_create dom_info;
+    int debug = 0, daemonize = 1, monitor = 1,
+        console_autoconnect = 0, vnc = 0, vncautopass = 0;
+
+    rc = libxl_domain_destroy(ctx, domid, 0);
+    if (rc) {
+        fprintf(stderr,"destroy failed (rc=%d)\n",rc);
+        goto out;
+    }
+
+    rc = libxl_disk_snapshot_revert(ctx, domid, snapshot->disks, snapshot->ndisks);
+    if (rc) {
+        fprintf(stderr,"disk revert failed (rc=%d)\n",rc);
+        goto out;
+    }
+
+    memset(&dom_info, 0, sizeof(dom_info));
+    dom_info.debug = debug;
+    dom_info.daemonize = daemonize;
+    dom_info.monitor = monitor;
+    dom_info.paused = paused ? 1 : 0;
+    dom_info.restore_file = snapshot->memory_path;
+    dom_info.migrate_fd = -1;
+    dom_info.vnc = vnc;
+    dom_info.vncautopass = vncautopass;
+    dom_info.console_autoconnect = console_autoconnect;
+
+    rc = create_domain(&dom_info);
+
+out:
+    return rc;
+
+}
+
+static void parse_domain_snapshot_config(const char *config_source,
+                                         const char *config_data,
+                                         int config_len,
+                                         domain_snapshot *snapshot,
+                                         int domid)
+
+{
+    const char *buf;
+    XLU_Config *config;
+    XLU_ConfigList *vbds;
+    int e;
+
+    config= xlu_cfg_init(stderr, config_source);
+    if (!config) {
+        fprintf(stderr, "Failed to allocate for configuration\n");
+        exit(1);
+    }
+
+    e= xlu_cfg_readdata(config, config_data, config_len);
+    if (e) {
+        fprintf(stderr, "Failed to parse config: %s\n", strerror(e));
+        exit(1);
+    }
+
+    if (!xlu_cfg_get_string (config, "name", &buf, 0))
+        snapshot->name = strdup(buf);
+
+    if (!xlu_cfg_get_string (config, "memory_path", &buf, 0))
+        snapshot->memory_path = strdup(buf);
+
+    if (!xlu_cfg_get_list (config, "disks", &vbds, 0, 0)) {
+        snapshot->ndisks = 0;
+        snapshot->disks = NULL;
+        while ((buf = xlu_cfg_get_listitem (vbds, snapshot->ndisks)) != NULL) {
+            libxl_device_disk disk;
+            libxl_disk_snapshot *disk_snapshot;
+            char *buf2 = strdup(buf);
+
+            snapshot->disks = (libxl_disk_snapshot*) realloc(snapshot->disks,
+                              sizeof (libxl_disk_snapshot) * (snapshot->ndisks + 1));
+            disk_snapshot = snapshot->disks + snapshot->ndisks;
+            libxl_disk_snapshot_init(disk_snapshot);
+            parse_disk_config(&config, buf2, &disk);
+            if (libxl_vdev_to_device_disk(ctx, domid, disk.vdev, &(disk_snapshot->disk))) {
+                fprintf(stderr, "Failed to parse disk snapshot config");
+                exit(1);
+            }
+
+            if (snapshot->name)
+                disk_snapshot->name = strdup(snapshot->name);
+
+            if (!disk.pdev_path || !strcmp(disk.pdev_path, "")) {
+                disk_snapshot->type = LIBXL_DISK_SNAPSHOT_TYPE_INTERNAL;
+            } else {
+                disk_snapshot->type = LIBXL_DISK_SNAPSHOT_TYPE_EXTERNAL;
+                disk_snapshot->u.external.external_path = disk.pdev_path;
+                if (!disk.format)
+                    disk_snapshot->u.external.external_format =
+                                               LIBXL_DISK_FORMAT_QCOW2;
+                else
+                    disk_snapshot->u.external.external_format = disk.format;
+            }
+            free(buf2);
+            snapshot->ndisks++;
+        }
+    }
+    xlu_cfg_destroy(config);
+}
+
+static char *snapshot_root(uint32_t domid)
+{
+    char *path = NULL;
+    libxl_dominfo info;
+    int rc;
+
+    rc = libxl_domain_info(ctx, &info, domid);
+    if (rc) return NULL;
+
+    asprintf(&path, "%s/"LIBXL_UUID_FMT,
+             XEN_SNAPSHOT_DIR, LIBXL_UUID_BYTES(info.uuid));
+    return path;
+}
+
+/* make directory to store domain snapshot data */
+static char *make_snapshot_path(uint32_t domid, char *dirname, bool dryrun)
+{
+    char *root, *path = NULL;
+    int rc, r;
+
+    root = snapshot_root(domid);
+    if (asprintf(&path, "%s/%s", root, dirname) < 0)
+        return NULL;
+
+    /* if dryrun, doesn't really make the directory */
+    if (dryrun)
+        return path;
+
+    for (;;) {
+        r = mkdir(root, 0600);
+        if (!r) break;
+        if (errno == EINTR) continue;
+        if (errno == EEXIST) break;
+        fprintf(stderr, "failed to create domain snapshot root dir %s", root);
+        rc = -1;
+        goto out;
+    }
+
+    for (;;) {
+        r = mkdir(path, 0600);
+        if (!r) break;
+        if (errno == EINTR) continue;
+        if (errno == EEXIST) break;
+        fprintf(stderr, "failed to create domain snapshot dir %s", path);
+        rc = -1;
+        goto out;
+    }
+
+    rc = 0;
+
+out:
+    return rc ? NULL : path;
+}
+
+/* if memory_path not specified, generate default location */
+static char *snapshot_default_memory_path(char *path, char *name)
+{
+    char *mem_path = NULL;
+    asprintf(&mem_path, "%s/%s.save", path, name);
+    return mem_path;
+}
+
+static yajl_gen_status domain_snapshot_gen_json(yajl_gen hand,
+                                                domain_snapshot *snapshot)
+{
+    yajl_gen_status s;
+    int i;
+
+    s = yajl_gen_map_open(hand);
+    if (s != yajl_gen_status_ok)
+        goto out;
+
+    s = yajl_gen_string(hand, (const unsigned char *)"path", sizeof("path")-1);
+    if (s != yajl_gen_status_ok)
+        goto out;
+
+    s = yajl_gen_string(hand, (const unsigned char *)(snapshot->path),
+                        strlen(snapshot->path));
+    if (s != yajl_gen_status_ok)
+        goto out;
+
+    s = yajl_gen_string(hand, (const unsigned char *)"name", sizeof("name")-1);
+    if (s != yajl_gen_status_ok)
+        goto out;
+
+    s = yajl_gen_string(hand, (const unsigned char *)(snapshot->name),
+                        strlen(snapshot->name));
+    if (s != yajl_gen_status_ok)
+        goto out;
+
+    s = yajl_gen_string(hand, (const unsigned char *)"memory_path",
+                        sizeof("memory_path")-1);
+    if (s != yajl_gen_status_ok)
+        goto out;
+
+    s = yajl_gen_string(hand, (const unsigned char *)(snapshot->memory_path),
+                        strlen(snapshot->memory_path));
+    if (s != yajl_gen_status_ok)
+        goto out;
+
+    s = yajl_gen_string(hand, (const unsigned char *)"disks", sizeof("disks")-1);
+    if (s != yajl_gen_status_ok)
+        goto out;
+
+    for (i = 0; i < snapshot->ndisks; i++) {
+        s = libxl_disk_snapshot_gen_json(hand, snapshot->disks + i);
+        if (s != yajl_gen_status_ok)
+            goto out;
+    }
+
+    s = yajl_gen_map_close(hand);
+    if (s != yajl_gen_status_ok)
+        goto out;
+
+out:
+    return s;
+}
+
+static yajl_gen_status printf_info_snapshot_one_json(yajl_gen hand, int domid,
+                                                    domain_snapshot *snapshot)
+{
+    yajl_gen_status s;
+
+    s = yajl_gen_map_open(hand);
+    if (s != yajl_gen_status_ok)
+        goto out;
+
+    s = yajl_gen_string(hand, (const unsigned char *)"domid",
+                        sizeof("domid")-1);
+    if (s != yajl_gen_status_ok)
+        goto out;
+    if (domid != -1)
+        s = yajl_gen_integer(hand, domid);
+    else
+        s = yajl_gen_null(hand);
+    if (s != yajl_gen_status_ok)
+        goto out;
+
+    s = yajl_gen_string(hand, (const unsigned char *)"snapshot",
+                        sizeof("snapshot")-1);
+    if (s != yajl_gen_status_ok)
+        goto out;
+
+    s = domain_snapshot_gen_json(hand, snapshot);
+    if (s != yajl_gen_status_ok)
+        goto out;
+
+    s = yajl_gen_map_close(hand);
+    if (s != yajl_gen_status_ok)
+        goto out;
+
+out:
+    return s;
+}
+
+static void printf_info_snapshot(enum output_format output_format,
+                                 int domid,
+                                 domain_snapshot *snapshot)
+{
+    const char *buf;
+    libxl_yajl_length len = 0;
+    yajl_gen_status s;
+    yajl_gen hand;
+
+    hand = libxl_yajl_gen_alloc(NULL);
+    if (!hand) {
+        fprintf(stderr, "unable to allocate JSON generator\n");
+        return;
+    }
+
+    s = printf_info_snapshot_one_json(hand, domid, snapshot);
+    if (s != yajl_gen_status_ok)
+        goto out;
+
+    s = yajl_gen_get_buf(hand, (const unsigned char **)&buf, &len);
+    if (s != yajl_gen_status_ok)
+        goto out;
+
+    puts(buf);
+
+out:
+    yajl_gen_free(hand);
+
+    if (s != yajl_gen_status_ok)
+        fprintf(stderr,
+                "unable to format domain config as JSON (YAJL:%d)\n", s);
+
+    if (ferror(stdout) || fflush(stdout)) { perror("stdout"); exit(-1); }
+}
+
+int main_snapshot_create(int argc, char **argv)
+{
+    uint32_t domid;
+    const char *config_file = NULL;
+    void *config_data = 0;
+    int config_len = 0;
+    char *path = NULL;
+    int live = 0, internal = 0, external = 0;
+    domain_snapshot snap;
+    libxl_dominfo dom_info;
+    struct timeval tv;
+    libxl_device_disk *disks = NULL;
+    int i, j, nb, opt;
+    int rc = 1;
+    static struct option opts[] = {
+        {"live", 0, 0, 'l'},
+        {"internal", 0, 0, 'i'},
+        {"external", 0, 0, 'e'},
+        {"path", 1, 0, 'p'},
+        COMMON_LONG_OPTS,
+        {0, 0, 0, 0}
+    };
+
+    SWITCH_FOREACH_OPT(opt, "liep:", opts, "snapshot-create", 1) {
+    case 'l':
+        live = 1;
+        break;
+    case 'i':
+        internal = 1;
+        break;
+    case 'e':
+        external = 1;
+        break;
+    case 'p':
+        path = optarg;
+        break;
+    }
+
+    assert(!(internal && external));
+
+    if (argc - optind > 2) {
+        help("snapshot-create");
+        return 2;
+    }
+
+    domid = find_domain(argv[optind++]);
+    if (argc > optind)
+        config_file = argv[optind];
+
+    if (libxl_domain_info(ctx, &dom_info, domid) < 0)
+        return 1;
+
+    if (dom_info.shutdown || dom_info.dying) {
+        fprintf(stderr, "Error: snapshot is not supported when domain "
+                        "is in shutdown or dying state.\n");
+        return 1;
+    }
+
+    disks = libxl_device_disk_list(ctx, domid, &nb);
+    for (i = 0; i < nb; i++) {
+        if (disks[i].is_cdrom) {
+            fprintf(stderr, "Error: snapshot is not supported when domain "
+                            "contains cdrom.\n");
+            goto out;
+        }
+    }
+
+    memset(&snap, 0, sizeof(snap));
+    gettimeofday(&tv, NULL);
+
+    if (config_file) {
+        if (libxl_read_file_contents(ctx, config_file,
+                                      &config_data, &config_len)) {
+            fprintf(stderr, "Failed to read config file: %s: %s\n",
+                           config_file, strerror(errno));
+            goto out;
+        }
+        parse_domain_snapshot_config(config_file, config_data,
+                                     config_len, &snap, domid);
+    }
+
+    if (path) {
+        struct stat st;
+        if(stat(path, &st) != 0 || !(st.st_mode & S_IFDIR)) {
+           fprintf(stderr, "Error: specified snapshot path "
+                   "%s does not exist\n", path);
+           goto out;
+        }
+        snap.path = path;
+    }
+
+    if (!snap.path) {
+        char *str;
+        if (asprintf(&str, "%lld", (long long)tv.tv_sec) < 0)
+            goto out;
+        if (!(snap.path = make_snapshot_path(domid, str, dryrun_only))) {
+            fprintf(stderr, "Failed to make default snapshot path\n");
+            free(str);
+            goto out;
+        }
+        free(str);
+    }
+
+    if (!snap.name) {
+        if (asprintf(&snap.name, "%lld", (long long)tv.tv_sec) < 0) {
+            fprintf(stderr, "Failed to generate default snapshot name\n");
+            goto out;
+        }
+    }
+
+    if (!snap.memory_path)
+        snap.memory_path = snapshot_default_memory_path(snap.path, snap.name);
+
+    if (internal || external) {
+        if (snap.disks) {
+            for (i = 0; i < snap.ndisks; i++) {
+                libxl_disk_snapshot_dispose(snap.disks + i);
+            }
+           free(snap.disks);
+        }
+        snap.ndisks = 0;
+    }
+
+    if (snap.ndisks < nb) {
+        /* set default value to remaining disks */
+        libxl_disk_snapshot *disksnap;
+        snap.disks = (libxl_disk_snapshot *)realloc(snap.disks,
+                                           sizeof(libxl_disk_snapshot) * nb);
+        for (i = 0; i < nb; i++) {
+            bool found = false;
+            for (j = 0; j < snap.ndisks; j++) {
+                if (!strcmp(snap.disks[j].disk.vdev, disks[i].vdev)) {
+                    found = true;
+                    break;
+                }
+            }
+            if (!found) {
+                disksnap = snap.disks + snap.ndisks;
+                libxl_disk_snapshot_init(disksnap);
+                disksnap->disk = disks[i];
+                if (internal)
+                    disksnap->type = LIBXL_DISK_SNAPSHOT_TYPE_INTERNAL;
+                else if (external) {
+                    disksnap->type = LIBXL_DISK_SNAPSHOT_TYPE_EXTERNAL;
+                    disksnap->u.external.external_format = LIBXL_DISK_FORMAT_QCOW2;
+                    if (asprintf(&disksnap->u.external.external_path, "%s/%s_%s.qcow2",
+                                 snap.path, disks[i].vdev, snap.name) < 0) {
+                        fprintf(stderr, "Fail to get disk snapshot configuration.\n");
+                        goto out;
+                    }
+                } else {
+                    switch (disks[i].format) {
+                    case LIBXL_DISK_FORMAT_QCOW2:
+                        disksnap->type = LIBXL_DISK_SNAPSHOT_TYPE_INTERNAL;
+                        break;
+                    default:
+                        disksnap->type = LIBXL_DISK_SNAPSHOT_TYPE_EXTERNAL;
+                        disksnap->u.external.external_format = LIBXL_DISK_FORMAT_QCOW2;
+                        if (asprintf(&disksnap->u.external.external_path, "%s/%s_%s.qcow2",
+                                     snap.path, disks[i].vdev, snap.name) < 0) {
+                            fprintf(stderr, "Fail to get disk snapshot configuration.\n");
+                            goto out;
+                        }
+                        break;
+                    }
+                }
+                snap.ndisks++;
+            }
+        }
+    }
+
+    for (i = 0; i < snap.ndisks; i++) {
+        if (!snap.disks[i].name)
+            snap.disks[i].name = strdup(snap.name);
+    }
+
+    if (dryrun_only) {
+        printf_info_snapshot(OUTPUT_FORMAT_JSON, domid, &snap);
+        rc = 0;
+        goto out;
+    }
+
+    /* do actual snapshot-create work. */
+    rc = domain_snapshot_create(domid, &snap, live);
+    if (rc < 0)
+        fprintf(stderr, "Error: domain_snapshot_create failed.\n");
+
+out:
+    free(disks);
+    free(config_data);
+    return rc;
+}
+
+int main_snapshot_revert(int argc, char **argv)
+{
+    int opt;
+    uint32_t domid;
+    char *config_file;
+    void *config_data = 0;
+    int config_len = 0;
+    int paused = 0;
+    domain_snapshot snap;
+    static struct option opts[] = {
+        {"pause", 0, 0, 'p'},
+        COMMON_LONG_OPTS,
+        {0, 0, 0, 0}
+    };
+    int i, rc;
+
+    SWITCH_FOREACH_OPT(opt, "p", opts, "snapshot-revert", 2) {
+    case 'p':
+        paused = 1;
+        break;
+    }
+
+    if (argc - optind != 2) {
+        help("snapshot-revert");
+        return 2;
+    }
+
+    domid = find_domain(argv[optind]);
+    config_file = argv[optind + 1];
+
+    memset(&snap, 0, sizeof(snap));
+
+    rc = libxl_read_file_contents(ctx, config_file,
+                                  &config_data, &config_len);
+    if (rc) {
+        fprintf(stderr, "Failed to read config file: %s: %s\n",
+                config_file, strerror(errno));
+        return 1;
+    }
+
+    parse_domain_snapshot_config(config_file, config_data,
+                                 config_len, &snap, domid);
+
+    if (!snap.name) {
+        fprintf(stderr, "snapshot name not specified\n");
+        rc = 1;
+        goto out;
+    }
+
+    if (!snap.memory_path) {
+        fprintf(stderr, "snapshot memory_path not specified\n");
+        rc = 1;
+        goto out;
+    }
+
+    if (access(snap.memory_path, R_OK)) {
+        fprintf(stderr, "snapshot memory_path doesn't exist\n");
+        rc = 1;
+        goto out;
+    }
+
+    if (!snap.disks) {
+        fprintf(stderr, "disk snapshots info not specified\n");
+        rc = 1;
+        goto out;
+    }
+
+    for (i = 0; i < snap.ndisks; i++) {
+        if (snap.disks[i].type != LIBXL_DISK_SNAPSHOT_TYPE_INTERNAL) {
+            fprintf(stderr, "Don't support revert from external "
+                            "disk snapshot\n");
+            rc = 1;
+            goto out;
+        }
+    }
+
+    rc = domain_snapshot_revert(domid, &snap, paused);
+
+out:
+    free(config_data);
+    return rc;
+}
 /*
  * Local variables:
  * mode: C
diff --git a/tools/libxl/xl_cmdtable.c b/tools/libxl/xl_cmdtable.c
index 0071f12..5f36949 100644
--- a/tools/libxl/xl_cmdtable.c
+++ b/tools/libxl/xl_cmdtable.c
@@ -551,6 +551,22 @@ struct cmd_spec cmd_table[] = {
     },
 
 #endif
+    { "snapshot-create",
+      &main_snapshot_create, 1, 1,
+      "create a snapshot (disk and RAM) of a domain.",
+      "[--live] [--internal|--external] [--path=path] <Domain> [<ConfigFile>]",
+      "-l,--live        take a live snapshot\n"
+      "-i,--internal    take internal disk snapshots to all disks\n"
+      "-e,--external    take external disk snapshots to all disks\n"
+      "-p,--path        path to store snapshot data"
+    },
+    { "snapshot-revert",
+      &main_snapshot_revert, 1, 1,
+      "revert domain to status of a snapshot.",
+      "[--pause] [--force] <Domain> <ConfigFile>",
+      "-p,--pause    keep domain paused after the revert\n"
+      "-f,--force    try harder on risky revert"
+    },
 };
 
 int cmdtable_len = sizeof(cmd_table)/sizeof(struct cmd_spec);
-- 
2.1.4
^ permalink raw reply related	[flat|nested] 9+ messages in thread
* [RFC PATCH 6/7] qmp: add qmp handlers to delete internal/external disk snapshot
  2015-08-11 10:44 [RFC PATCH 0/7] domain snapshot implementation Chunyan Liu
                   ` (4 preceding siblings ...)
  2015-08-11 10:44 ` [RFC PATCH 5/7] xl: add domain snapshot commands Chunyan Liu
@ 2015-08-11 10:44 ` Chunyan Liu
  2015-08-11 10:44 ` [RFC PATCH 7/7] libxl: add APIs to delete internal/external disk snapshots Chunyan Liu
  2015-08-31  6:11 ` [RFC PATCH 0/7] domain snapshot implementation Chun Yan Liu
  7 siblings, 0 replies; 9+ messages in thread
From: Chunyan Liu @ 2015-08-11 10:44 UTC (permalink / raw)
  To: xen-devel; +Cc: wei.liu2, jfehlig, Ian.Jackson, ian.campbell, Chunyan Liu
Xl doesn't maintain domain snapshot info and has no idea of snapshot
info and related files after creation, so it doesn't supply domain
snapshot delete command. These qmp handlers won't be used by xl.
But for libvirt, it maintains domain snapshot info itself and supplies
snapshot delete commands, and so needs APIs from libxl to delete
internal/external disk snapshots. To libvirt, these qmp handlers
are userful.
Signed-off-by: Chunyan Liu <cyliu@suse.com>
---
 tools/libxl/libxl.h          |  17 +++++
 tools/libxl/libxl_internal.h |  28 ++++++++
 tools/libxl/libxl_qmp.c      | 158 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 203 insertions(+)
diff --git a/tools/libxl/libxl.h b/tools/libxl/libxl.h
index d60f139..412a42f 100644
--- a/tools/libxl/libxl.h
+++ b/tools/libxl/libxl.h
@@ -1730,6 +1730,23 @@ void libxl_psr_cat_info_list_free(libxl_psr_cat_info *list, int nr);
 #endif
 
 /* Domain snapshot related APIs */
+
+/* structure to retrieve qmp block job status */
+typedef struct libxl_block_job_info
+{
+     char *disk_vdev;
+     const char *type;
+     unsigned long speed;
+     /* The following fields provide an indication of block job progress.
+      * @current indicates the current position and will be between 0 and @end.
+      * @end is the final cursor position for this operation and represents
+      * completion.
+      * To approximate progress, divide @cur by @end.
+      */
+      unsigned long long current;
+      unsigned long long end;
+} libxl_block_job_info;
+
 int libxl_disk_snapshot_create(libxl_ctx *ctx, uint32_t domid,
                                libxl_disk_snapshot *snapshot, int nb);
 int libxl_disk_snapshot_revert(libxl_ctx *ctx, uint32_t domid,
diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h
index f24e0af..a6456e8 100644
--- a/tools/libxl/libxl_internal.h
+++ b/tools/libxl/libxl_internal.h
@@ -1739,6 +1739,34 @@ _hidden int libxl__qmp_cpu_add(libxl__gc *gc, int domid, int index);
 _hidden int libxl__qmp_disk_snapshot_transaction(libxl__gc *gc, int domid,
                                                 libxl_disk_snapshot *snapshot,
                                                 int nb);
+/* Delete a disk snapshot */
+_hidden int libxl__qmp_disk_snapshot_delete(libxl__gc *gc, int domid,
+                                           libxl_disk_snapshot *snapshot);
+/* shorten backing file chain. Merge base to top */
+_hidden int libxl__qmp_block_commit(libxl__gc *gc, uint32_t domid,
+                                    libxl_device_disk *disk,
+                                    const char *base, const char *top,
+                                    const char *backing_file,
+                                    unsigned long bandwidth);
+/* shorten backing file chain. Merge top to base */
+_hidden int libxl__qmp_block_stream(libxl__gc *gc, uint32_t domid,
+                                    libxl_device_disk *disk,
+                                    const char *base,
+                                    const char *backing_file,
+                                    unsigned long long bandwidth,
+                                    const char *error);
+/* query qmp block job status */
+_hidden int libxl__qmp_query_block_job(libxl__gc *gc, uint32_t domid,
+                                       libxl_device_disk *disk,
+                                       libxl_block_job_info *info);
+/* cancel a qmp block job */
+_hidden int libxl__qmp_block_job_cancel(libxl__gc *gc, uint32_t domid,
+                                        libxl_device_disk *disk,
+                                        bool force);
+
+/* complete a qmp block job */
+_hidden int libxl__qmp_block_job_complete(libxl__gc *gc, uint32_t domid,
+                                          libxl_device_disk *disk);
 /* close and free the QMP handler */
 _hidden void libxl__qmp_close(libxl__qmp_handler *qmp);
 /* remove the socket file, if the file has already been removed,
diff --git a/tools/libxl/libxl_qmp.c b/tools/libxl/libxl_qmp.c
index 2216511..09cb628 100644
--- a/tools/libxl/libxl_qmp.c
+++ b/tools/libxl/libxl_qmp.c
@@ -1034,6 +1034,164 @@ int libxl__qmp_disk_snapshot_transaction(libxl__gc *gc, int domid,
     return qmp_run_command(gc, domid, "transaction", args, NULL, NULL);
 }
 
+int libxl__qmp_disk_snapshot_delete(libxl__gc *gc, int domid,
+                                   libxl_disk_snapshot *snapshot)
+{
+    libxl__json_object *args = NULL;
+
+    if (snapshot->type == LIBXL_DISK_SNAPSHOT_TYPE_EXTERNAL) {
+        LOG(ERROR, "QMP doesn't support deleting external disk snapshot");
+        return -1;
+    }
+
+    if (snapshot->type != LIBXL_DISK_SNAPSHOT_TYPE_INTERNAL) {
+        LOG(ERROR, "Invalid disk snapshot type");
+        return -1;
+    }
+
+    qmp_parameters_add_string(gc, &args, "device", snapshot->disk.vdev);
+    qmp_parameters_add_string(gc, &args, "name", snapshot->name);
+
+    return qmp_run_command(gc, domid, "blockdev-snapshot-delete-internal-sync",
+                           args, NULL, NULL);
+}
+
+/* send qmp 'block-commit' command
+ * .args_type  = "device:B,base:s?,top:s?,backing-file:s?,speed:o?"
+ * According to qemu document:
+ * If top == active image, the job will not be completed by itself,
+ * user needs to complete the job with the block-job-complete
+ * command after getting ready.
+ */
+int libxl__qmp_block_commit(libxl__gc *gc, uint32_t domid,
+                            libxl_device_disk *disk,
+                            const char *base, const char *top,
+                            const char *backing_file,
+                            unsigned long bandwidth)
+{
+    libxl__json_object *args = NULL;
+
+    qmp_parameters_add_string(gc, &args, "device", disk->vdev);
+    if (base != NULL)
+        qmp_parameters_add_string(gc, &args, "base", base);
+    if (top != NULL)
+        qmp_parameters_add_string(gc, &args, "top", top);
+    if (backing_file != NULL)
+        qmp_parameters_add_string(gc, &args, "backing-file", backing_file);
+    if (!bandwidth)
+        qmp_parameters_add_integer(gc, &args, "speed", bandwidth);
+
+    return qmp_run_command(gc, domid, "block-commit", args, NULL, NULL);
+}
+
+/* send qmp 'block-stream' command
+ * .args_type  = "device:B,base:s?,speed:o?,backing-file:s?,on-error:s?"
+ */
+int libxl__qmp_block_stream(libxl__gc *gc, uint32_t domid,
+                            libxl_device_disk *disk,
+                            const char *base,
+                            const char *backing_file,
+                            unsigned long long bandwidth,
+                            const char *error)
+{
+    libxl__json_object *args = NULL;
+
+    qmp_parameters_add_string(gc, &args, "device", disk->vdev);
+    if (base != NULL)
+        qmp_parameters_add_string(gc, &args, "base", base);
+    if (backing_file != NULL)
+        qmp_parameters_add_string(gc, &args, "backing-file", backing_file);
+    if (!bandwidth)
+        qmp_parameters_add_integer(gc, &args, "speed", bandwidth);
+    if (error != NULL)
+        qmp_parameters_add_string(gc, &args, "on-error", error);
+
+    return qmp_run_command(gc, domid, "block-stream", args, NULL, NULL);
+}
+
+/* send qmp 'block-job-cancel' command
+ *   .args_type  = "device:B,force:b?"
+ */
+int libxl__qmp_block_job_cancel(libxl__gc *gc, uint32_t domid,
+                                libxl_device_disk *disk,
+                                bool force)
+{
+    libxl__json_object *args = NULL;
+
+    qmp_parameters_add_string(gc, &args, "device", disk->vdev);
+    if (force)
+        qmp_parameters_add_bool(gc, &args, "force", force);
+
+    return qmp_run_command(gc, domid, "block-job-cancel", args, NULL, NULL);
+}
+
+/* send qmp 'block-job-complete' command
+ *   .args_type  = "device:B",
+ */
+int libxl__qmp_block_job_complete(libxl__gc *gc, uint32_t domid,
+                                  libxl_device_disk *disk)
+{
+    libxl__json_object *args = NULL;
+
+    qmp_parameters_add_string(gc, &args, "device", disk->vdev);
+
+    return qmp_run_command(gc, domid, "block-job-complete", args, NULL, NULL);
+}
+
+static int qmp_query_block_jobs_callback(libxl__qmp_handler *qmp,
+                                         const libxl__json_object *response,
+                                         void *opaque)
+{
+    /* export response for upper layer to handle */
+    libxl_block_job_info *info = opaque;
+    const libxl__json_object *obj = NULL;
+    int i;
+    bool found = false;
+
+    for (i = 0; (obj = libxl__json_array_get(response, i)); i++) {
+        const libxl__json_object *o = NULL;
+        const char *device;
+
+        if (!libxl__json_object_is_map(obj))
+            continue;
+        o = libxl__json_map_get("device", obj, JSON_STRING);
+        device = libxl__json_object_get_string(o);
+        if (!device || !strcmp(device, info->disk_vdev))
+            continue;
+
+        o = libxl__json_map_get("type", obj, JSON_STRING);
+        if (!o)
+            continue;
+        info->type = libxl__json_object_get_string(o);
+
+        o = libxl__json_map_get("speed", obj, JSON_INTEGER);
+        info->speed = libxl__json_object_get_integer(o);
+
+        o = libxl__json_map_get("offset", obj, JSON_INTEGER);
+        info->current = libxl__json_object_get_integer(o);
+
+        o = libxl__json_map_get("len", obj, JSON_INTEGER);
+        info->end = libxl__json_object_get_integer(o);
+
+        found = true;
+        break;
+    }
+
+    if (!found)
+        return -1;
+
+    return 0;
+}
+
+int libxl__qmp_query_block_job(libxl__gc *gc, uint32_t domid,
+                               libxl_device_disk *disk,
+                               libxl_block_job_info *info)
+{
+    info->disk_vdev = libxl__strdup(gc, disk->vdev);
+    return qmp_run_command(gc, domid, "query-block-jobs", NULL,
+                           &qmp_query_block_jobs_callback, info);
+}
+
 int libxl__qmp_initializations(libxl__gc *gc, uint32_t domid,
                                const libxl_domain_config *guest_config)
 {
-- 
2.1.4
^ permalink raw reply related	[flat|nested] 9+ messages in thread
* [RFC PATCH 7/7] libxl: add APIs to delete internal/external disk snapshots
  2015-08-11 10:44 [RFC PATCH 0/7] domain snapshot implementation Chunyan Liu
                   ` (5 preceding siblings ...)
  2015-08-11 10:44 ` [RFC PATCH 6/7] qmp: add qmp handlers to delete internal/external disk snapshot Chunyan Liu
@ 2015-08-11 10:44 ` Chunyan Liu
  2015-08-31  6:11 ` [RFC PATCH 0/7] domain snapshot implementation Chun Yan Liu
  7 siblings, 0 replies; 9+ messages in thread
From: Chunyan Liu @ 2015-08-11 10:44 UTC (permalink / raw)
  To: xen-devel; +Cc: wei.liu2, jfehlig, Ian.Jackson, ian.campbell, Chunyan Liu
Currently this group of APIs are not used by xl toolstack since xl
doesn't maintain domain snapshot info and so depends on user to
delete things.
But for libvirt, they are very useful since libvirt maintains domain
snapshot info itself and needs these APIs to delete
internal/external disk snapshots.
Signed-off-by: Chunyan Liu <cyliu@suse.com>
---
 tools/libxl/libxl.h          |  28 ++++++++++++
 tools/libxl/libxl_snapshot.c | 102 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 130 insertions(+)
diff --git a/tools/libxl/libxl.h b/tools/libxl/libxl.h
index 412a42f..1383b92 100644
--- a/tools/libxl/libxl.h
+++ b/tools/libxl/libxl.h
@@ -1752,6 +1752,34 @@ int libxl_disk_snapshot_create(libxl_ctx *ctx, uint32_t domid,
 int libxl_disk_snapshot_revert(libxl_ctx *ctx, uint32_t domid,
                                libxl_disk_snapshot *snapshot, int nb);
 
+/* delete internal disk snapshot */
+int libxl_disk_snapshot_delete(libxl_ctx *ctx, uint32_t domid,
+                               libxl_disk_snapshot *snapshot, int nb);
+/* next 4 functions deal with external disk snapshot */
+/* shorten backing file chain. Merge from top to base */
+int libxl_domain_block_rebase(libxl_ctx *ctx, uint32_t domid,
+                              libxl_device_disk *disk,
+                              const char *base,
+                              const char *backing_file,
+                              unsigned long long bandwidth);
+/* shorten backing file chain. Merge from base to top */
+int libxl_domain_block_commit(libxl_ctx *ctx, uint32_t domid,
+                              libxl_device_disk *disk,
+                              const char *top,
+                              const char *base,
+                              const char *backing_file,
+                              unsigned long long bandwidth);
+/* query a block job status, can get job type, speed, progress status */
+int libxl_domain_block_job_query(libxl_ctx *ctx, uint32_t domid,
+                                 libxl_device_disk *disk,
+                                 libxl_block_job_info *info);
+/* abort a block job. If the job is finished, complete it.
+ * otherwise, cancel it.
+ */
+int libxl_domain_block_job_abort(libxl_ctx *ctx, uint32_t domid,
+                                 libxl_device_disk *disk,
+                                 bool force);
+
 /* misc */
 
 /* Each of these sets or clears the flag according to whether the
diff --git a/tools/libxl/libxl_snapshot.c b/tools/libxl/libxl_snapshot.c
index 34d36ef..9b139e6 100644
--- a/tools/libxl/libxl_snapshot.c
+++ b/tools/libxl/libxl_snapshot.c
@@ -217,3 +217,105 @@ int libxl_disk_snapshot_revert(libxl_ctx *ctx, uint32_t domid,
     }
     return rc;
 }
+
+int libxl_disk_snapshot_delete(libxl_ctx *ctx, uint32_t domid,
+                               libxl_disk_snapshot *snapshot, int nb)
+{
+    int rc = 0;
+    int i;
+
+    GC_INIT(ctx);
+    for(i = 0; i < nb; i++ ) {
+        if (snapshot[i].type == LIBXL_DISK_SNAPSHOT_TYPE_EXTERNAL) {
+            LOG(WARN, "libxl_disk_snapshot_delete: external disk snapshot "
+                "cannot be deleted. Please use libxl_domain_block_commit and "
+                "libxl_domain_block_rebase to handle that.");
+            continue;
+        }
+
+        rc = libxl__qmp_disk_snapshot_delete(gc, domid, &snapshot[i]);
+        if ( rc )
+            goto err;
+    }
+err:
+    if (rc)
+        LOG(ERROR, "domain disk snapshot delete fail\n");
+
+    GC_FREE;
+    return rc;
+}
+
+int libxl_domain_block_rebase(libxl_ctx *ctx, uint32_t domid,
+                              libxl_device_disk *disk,
+                              const char *base,
+                              const char *backing_file,
+                              unsigned long long bandwidth)
+{
+    GC_INIT(ctx);
+    int rc;
+
+    rc = libxl__qmp_block_stream(gc, domid, disk, base,
+                                 backing_file, bandwidth, NULL);
+    GC_FREE;
+    return rc;
+}
+
+int libxl_domain_block_commit(libxl_ctx *ctx, uint32_t domid,
+                              libxl_device_disk *disk,
+                              const char *top,
+                              const char *base,
+                              const char *backing_file,
+                              unsigned long long bandwidth)
+
+{
+    GC_INIT(ctx);
+    int rc;
+
+    rc = libxl__qmp_block_commit(gc, domid, disk, top, base,
+                                 backing_file, bandwidth);
+
+    GC_FREE;
+    return rc;
+}
+
+/* query block job status */
+int libxl_domain_block_job_query(libxl_ctx *ctx, uint32_t domid,
+                                 libxl_device_disk *disk,
+                                 libxl_block_job_info *info)
+{
+    GC_INIT(ctx);
+    int rc;
+
+    rc = libxl__qmp_query_block_job(gc, domid, disk, info);
+
+    GC_FREE;
+    return rc;
+}
+
+/* Abort block job:
+ * If block job is already finished, call "block_job_complete" qmp;
+ * otherwise, call "block_job_cancel" qmp command.
+ */
+int libxl_domain_block_job_abort(libxl_ctx *ctx, uint32_t domid,
+                                 libxl_device_disk *disk,
+                                 bool force)
+{
+    GC_INIT(ctx);
+    libxl_block_job_info info;
+    int rc;
+
+    /* query block job info */
+    if (libxl_domain_block_job_query(ctx, domid, disk, &info) < 0) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    if (info.current == info.end)
+        rc = libxl__qmp_block_job_complete(gc, domid, disk);
+    else
+        rc = libxl__qmp_block_job_cancel(gc, domid, disk, force);
+
+out:
+    GC_FREE;
+    return rc;
+}
-- 
2.1.4
^ permalink raw reply related	[flat|nested] 9+ messages in thread
* Re: [RFC PATCH 0/7] domain snapshot implementation
  2015-08-11 10:44 [RFC PATCH 0/7] domain snapshot implementation Chunyan Liu
                   ` (6 preceding siblings ...)
  2015-08-11 10:44 ` [RFC PATCH 7/7] libxl: add APIs to delete internal/external disk snapshots Chunyan Liu
@ 2015-08-31  6:11 ` Chun Yan Liu
  7 siblings, 0 replies; 9+ messages in thread
From: Chun Yan Liu @ 2015-08-31  6:11 UTC (permalink / raw)
  To: xen-devel, Chun Yan Liu; +Cc: Ian.Jackson, Jim Fehlig, wei.liu2, ian.campbell
ping?
>>> On 8/11/2015 at 06:44 PM, in message
<1439289896-27849-1-git-send-email-cyliu@suse.com>, Chunyan Liu
<cyliu@suse.com> wrote: 
> Add vm snapshot implementation. Support snapshot-create and 
> snapshot-revert. 
>  
> Current Limitations: 
>  
> About disk snapshot create, there are many cases: 
>  - qdisk, internal, should calls qmp command to do the work. 
>  - qdisk, external, should calls qmp command to do the work, qemu 
>    will replace disk backend file after creating external snapshot. 
>  - nonqdisk, internal, should call 'qemu-img snapshot' to do the work. 
>  - nonqdisk, external, should call 'qemu-img create' to create a new file 
>    with the original disk file as backing file. And libxl should replace 
>    domain disk from original disk to the new file. 
>  
>  To the last case, during domain snapshot, between domain suspend and 
>  resume, how to replace the disk backend file from libxl? Especially if 
>  disk file format is changed (original disk backend file is 'raw', new file 
>  is 'qcow2')? 
>  
>  Considering this, currently I exclude the non-qdisk cases, let the API 
>  support qdisk only. About the non-qdisk and external case, any suggestion? 
>  
> About disk snapshot revert: 
>  
>  Reverting from external disk snapshot is actually starting domain from a 
>  specified backing file, since backing file should be kept read-only, that 
>  will involve block copy operation. Currently this case is not supported. 
>  
>  Only support reverting from internal disk snapshot. 
>  
> Design document: 
>  
> Latest design document is just posted. 
>  
>  
> Chunyan Liu (7): 
>   libxl_types.idl: add definitions for vm snapshot 
>   qmp: add qmp handlers to create disk snapshots 
>   libxl: save disk format to xenstore 
>   libxl: add snapshot APIs 
>   xl: add domain snapshot commands 
>   qmp: add qmp handlers to delete internal/external disk snapshot 
>   libxl: add APIs to delete internal/external disk snapshots 
>  
>  Config.mk                            |   2 +- 
>  config/Paths.mk.in                   |   1 + 
>  configure                            |   3 + 
>  docs/man/xl.snapshot.conf.pod.5      |  59 +++ 
>  m4/paths.m4                          |   3 + 
>  tools/configure                      |   3 + 
>  tools/examples/snapshot.cfg.external |   4 + 
>  tools/examples/snapshot.cfg.internal |   4 + 
>  tools/libxl/Makefile                 |   2 + 
>  tools/libxl/libxl.c                  |  10 +- 
>  tools/libxl/libxl.h                  |  51 +++ 
>  tools/libxl/libxl_internal.h         |  38 ++ 
>  tools/libxl/libxl_qmp.c              | 224 ++++++++++++ 
>  tools/libxl/libxl_snapshot.c         | 321 +++++++++++++++++ 
>  tools/libxl/libxl_types.idl          |  31 ++ 
>  tools/libxl/libxl_types_internal.idl |   8 + 
>  tools/libxl/libxl_utils.c            |  16 + 
>  tools/libxl/libxl_utils.h            |   1 + 
>  tools/libxl/xl.h                     |   2 + 
>  tools/libxl/xl_cmdimpl.c             | 677  
> +++++++++++++++++++++++++++++++++++ 
>  tools/libxl/xl_cmdtable.c            |  16 + 
>  21 files changed, 1474 insertions(+), 2 deletions(-) 
>  create mode 100644 docs/man/xl.snapshot.conf.pod.5 
>  create mode 100644 tools/examples/snapshot.cfg.external 
>  create mode 100644 tools/examples/snapshot.cfg.internal 
>  create mode 100644 tools/libxl/libxl_snapshot.c 
^ permalink raw reply	[flat|nested] 9+ messages in thread
end of thread, other threads:[~2015-08-31  6:11 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-08-11 10:44 [RFC PATCH 0/7] domain snapshot implementation Chunyan Liu
2015-08-11 10:44 ` [RFC PATCH 1/7] add definitions for vm snapshot Chunyan Liu
2015-08-11 10:44 ` [RFC PATCH 2/7] qmp: add qmp handlers to create disk snapshots Chunyan Liu
2015-08-11 10:44 ` [RFC PATCH 3/7] libxl: save disk format to xenstore Chunyan Liu
2015-08-11 10:44 ` [RFC PATCH 4/7] libxl: add snapshot APIs Chunyan Liu
2015-08-11 10:44 ` [RFC PATCH 5/7] xl: add domain snapshot commands Chunyan Liu
2015-08-11 10:44 ` [RFC PATCH 6/7] qmp: add qmp handlers to delete internal/external disk snapshot Chunyan Liu
2015-08-11 10:44 ` [RFC PATCH 7/7] libxl: add APIs to delete internal/external disk snapshots Chunyan Liu
2015-08-31  6:11 ` [RFC PATCH 0/7] domain snapshot implementation Chun Yan Liu
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).