Add a new 'snapshot-import' command for bulk import. For bulk
XML, it was easier to just add new flags to the existing
'snapshot-dumpxml' than to figure out a new command name.
I debated about whether omitting the snapshotname should be
enough to trigger the new API call, or whether to require a
new bool --all; in the end, I went with the latter.
Signed-off-by: Eric Blake <eblake(a)redhat.com>
---
tools/virsh-snapshot.c | 111 ++++++++++++++++++++++++++++++++++++-----
tools/virsh.pod | 15 +++++-
2 files changed, 112 insertions(+), 14 deletions(-)
diff --git a/tools/virsh-snapshot.c b/tools/virsh-snapshot.c
index 31153f5b10..2a2fc0108b 100644
--- a/tools/virsh-snapshot.c
+++ b/tools/virsh-snapshot.c
@@ -1,7 +1,7 @@
/*
* virsh-snapshot.c: Commands to manage domain snapshot
*
- * Copyright (C) 2005, 2007-2016 Red Hat, Inc.
+ * Copyright (C) 2005-2019 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -444,6 +444,61 @@ cmdSnapshotCreateAs(vshControl *ctl, const vshCmd *cmd)
return ret;
}
+/*
+ * "snapshot-import" command
+ */
+static const vshCmdInfo info_snapshot_import[] = {
+ {.name = "help",
+ .data = N_("Bulk import snapshots from XML")
+ },
+ {.name = "desc",
+ .data = N_("Import the metadata for multiple snapshots from XML")
+ },
+ {.name = NULL}
+};
+
+static const vshCmdOptDef opts_snapshot_import[] = {
+ VIRSH_COMMON_OPT_DOMAIN_FULL(0),
+ {.name = "xmlfile",
+ .type = VSH_OT_DATA,
+ .flags = VSH_OFLAG_REQ,
+ .help = N_("domain snapshots XML"),
+ },
+ {.name = NULL}
+};
+
+static bool
+cmdSnapshotImport(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom = NULL;
+ bool ret = false;
+ const char *from = NULL;
+ char *buffer = NULL;
+ int count;
+
+ if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
+ goto cleanup;
+
+ if (vshCommandOptStringReq(ctl, cmd, "xmlfile", &from) < 0)
+ goto cleanup;
+ if (virFileReadAll(from, VSH_MAX_XML_FILE, &buffer) < 0) {
+ vshSaveLibvirtError();
+ goto cleanup;
+ }
+
+ if ((count = virDomainImportSnapshotsXML(dom, buffer, 0)) < 0)
+ goto cleanup;
+ vshPrint(ctl, _("Imported %d snapshots"), count);
+
+ ret = true;
+
+ cleanup:
+ VIR_FREE(buffer);
+ virshDomainFree(dom);
+
+ return ret;
+}
+
/* Helper for resolving {--current | --ARG name} into a snapshot
* belonging to DOM. If EXCLUSIVE, fail if both --current and arg are
* present. On success, populate *SNAP and *NAME, before returning 0.
@@ -1664,11 +1719,18 @@ static const vshCmdInfo info_snapshot_dumpxml[] = {
static const vshCmdOptDef opts_snapshot_dumpxml[] = {
VIRSH_COMMON_OPT_DOMAIN_FULL(0),
{.name = "snapshotname",
- .type = VSH_OT_DATA,
- .flags = VSH_OFLAG_REQ,
+ .type = VSH_OT_STRING,
.help = N_("snapshot name"),
.completer = virshSnapshotNameCompleter,
},
+ {.name = "all",
+ .type = VSH_OT_BOOL,
+ .help = N_("list all snapshots at once"),
+ },
+ {.name = "topological",
+ .type = VSH_OT_BOOL,
+ .help = N_("with --all, ensure listing is topologically sorted"),
+ },
{.name = "security-info",
.type = VSH_OT_BOOL,
.help = N_("include security sensitive information in XML dump")
@@ -1681,32 +1743,49 @@ cmdSnapshotDumpXML(vshControl *ctl, const vshCmd *cmd)
{
virDomainPtr dom = NULL;
bool ret = false;
- const char *name = NULL;
+ const char *snapshotname = NULL;
virDomainSnapshotPtr snapshot = NULL;
char *xml = NULL;
unsigned int flags = 0;
+ bool all = vshCommandOptBool(cmd, "all");
+
+ if (vshCommandOptStringReq(ctl, cmd, "snapshotname", &snapshotname)
< 0)
+ return false;
+
+ VSH_EXCLUSIVE_OPTIONS_VAR(snapshotname, all);
+ VSH_EXCLUSIVE_OPTIONS("snapshotname", "topological");
if (vshCommandOptBool(cmd, "security-info"))
- flags |= VIR_DOMAIN_XML_SECURE;
-
- if (vshCommandOptStringReq(ctl, cmd, "snapshotname", &name) < 0)
- return false;
+ flags |= all ? VIR_DOMAIN_GET_SNAPSHOTS_XML_SECURE :
+ VIR_DOMAIN_XML_SECURE;
+ if (vshCommandOptBool(cmd, "topological"))
+ flags |= VIR_DOMAIN_GET_SNAPSHOTS_XML_TOPOLOGICAL;
if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
return false;
- if (!(snapshot = virDomainSnapshotLookupByName(dom, name, 0)))
- goto cleanup;
+ if (all) {
+ if (!(xml = virDomainGetSnapshotsXMLDesc(dom, flags)))
+ goto cleanup;
+ } else {
+ if (!snapshotname) {
+ vshError(ctl, "%s", _("either snapshotname or --all
required"));
+ goto cleanup;
+ }
+ if (!(snapshot = virDomainSnapshotLookupByName(dom, snapshotname, 0)))
+ goto cleanup;
- if (!(xml = virDomainSnapshotGetXMLDesc(snapshot, flags)))
- goto cleanup;
+ if (!(xml = virDomainSnapshotGetXMLDesc(snapshot, flags)))
+ goto cleanup;
+ }
vshPrint(ctl, "%s", xml);
ret = true;
cleanup:
VIR_FREE(xml);
- virshDomainSnapshotFree(snapshot);
+ if (!all)
+ virshDomainSnapshotFree(snapshot);
virshDomainFree(dom);
return ret;
@@ -1952,6 +2031,12 @@ const vshCmdDef snapshotCmds[] = {
.info = info_snapshot_create_as,
.flags = 0
},
+ {.name = "snapshot-import",
+ .handler = cmdSnapshotImport,
+ .opts = opts_snapshot_import,
+ .info = info_snapshot_import,
+ .flags = 0,
+ },
{.name = "snapshot-current",
.handler = cmdSnapshotCurrent,
.opts = opts_snapshot_current,
diff --git a/tools/virsh.pod b/tools/virsh.pod
index 66e2bf24ec..28adcb85c3 100644
--- a/tools/virsh.pod
+++ b/tools/virsh.pod
@@ -4680,6 +4680,15 @@ If I<--live> is specified, libvirt takes the snapshot while
the guest is
running. This increases the size of the memory image of the external
snapshot. This is currently supported only for external full system snapshots.
+=item B<snapshot-import> I<domain> I<xmlfile>
+
+Import the metadata for all snapshots, for a domain with no existing
+snapshot metadata. This is useful for migrating snapshots from one
+host (with B<snapshot-dumpxml --all --security-info>) to another.
+
+This command is a shortcut to using B<snapshot-create --redefine> one
+snapshot at a time.
+
=item B<snapshot-current> I<domain> {[I<--name>] |
[I<--security-info>]
| [I<snapshotname>]}
@@ -4786,9 +4795,13 @@ that use internal storage of existing disk images. If
I<--external>
is specified, the list will be filtered to snapshots that use external
files for disk images or memory state.
-=item B<snapshot-dumpxml> I<domain> I<snapshot>
[I<--security-info>]
+=item B<snapshot-dumpxml> I<domain> {I<snapshot> | I<--all>
[I<--topological>]}
+[I<--security-info>]
Output the snapshot XML for the domain's snapshot named I<snapshot>.
+Alternatively, if I<--all> is passed instead of I<snapshot> output XML
+describing all snapshots at once, where I<--topological> can also be
+used to ensure no child is listed before a parent.
Using I<--security-info> will also include security sensitive information.
Use B<snapshot-current> to easily access the XML of the current snapshot.
--
2.20.1