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

[Xen-devel] [RFC PATCH 5/7] xl: add domain snapshot commands



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@xxxxxxxx>
---
 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


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.