Wire up the new snapshot creation flags in virsh. For convenience,
teach 'snapshot-current' how to make an existing snapshot become
current (can be used after upgrading to newer libvirt to recover
from the fact that the older libvirt lost track of the current
snapshot after a restart). The snapshot-create-as command is
intentionally not taught --redefine or --current, as this would
imply adding a lot of other options for everything else that can
appear in the <domainsnapshot> xml, but which is normally read-only.
Besides, redefining will usually be done on files created by
snapshot-dumpxml, rather than something built up by hand on the
command line. And now that we can redefine, we can edit.
* tools/virsh.c (cmdSnapshotCreate): Add --redefine, --current,
and --no-metadata.
(cmdSnapshotCreateAs): Add --no-metadata.
(cmdSnapshotCurrent): Add snapshotname to alter current snapshot.
(cmdSnapshotEdit): New command.
* tools/virsh.pod (snapshot-create, snapshot-create-as)
(snapshot-current, snapshot-edit): Document these.
---
tools/virsh.c | 162 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
tools/virsh.pod | 71 ++++++++++++++++++++-----
2 files changed, 215 insertions(+), 18 deletions(-)
diff --git a/tools/virsh.c b/tools/virsh.c
index f048dd5..e6a053b 100644
--- a/tools/virsh.c
+++ b/tools/virsh.c
@@ -11921,7 +11921,10 @@ vshSnapshotCreate(vshControl *ctl, virDomainPtr dom, const char
*buffer,
if (snapshot == NULL)
goto cleanup;
- doc = virDomainSnapshotGetXMLDesc(snapshot, 0);
+ if (flags & VIR_DOMAIN_SNAPSHOT_CREATE_NO_METADATA)
+ doc = vshStrdup(ctl, buffer);
+ else
+ doc = virDomainSnapshotGetXMLDesc(snapshot, 0);
if (!doc)
goto cleanup;
@@ -11965,6 +11968,9 @@ static const vshCmdInfo info_snapshot_create[] = {
static const vshCmdOptDef opts_snapshot_create[] = {
{"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or
uuid")},
{"xmlfile", VSH_OT_DATA, 0, N_("domain snapshot XML")},
+ {"redefine", VSH_OT_BOOL, 0, N_("redefine metadata for existing
snapshot")},
+ {"current", VSH_OT_BOOL, 0, N_("with redefine, set current
snapshot")},
+ {"no-metadata", VSH_OT_BOOL, 0, N_("take snapshot but create no
metadata")},
{NULL, 0, 0, NULL}
};
@@ -11975,6 +11981,14 @@ cmdSnapshotCreate(vshControl *ctl, const vshCmd *cmd)
bool ret = false;
const char *from = NULL;
char *buffer = NULL;
+ unsigned int flags = 0;
+
+ if (vshCommandOptBool(cmd, "redefine"))
+ flags |= VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE;
+ if (vshCommandOptBool(cmd, "current"))
+ flags |= VIR_DOMAIN_SNAPSHOT_CREATE_CURRENT;
+ if (vshCommandOptBool(cmd, "no-metadata"))
+ flags |= VIR_DOMAIN_SNAPSHOT_CREATE_NO_METADATA;
if (!vshConnectionUsability(ctl, ctl->conn))
goto cleanup;
@@ -12000,7 +12014,7 @@ cmdSnapshotCreate(vshControl *ctl, const vshCmd *cmd)
goto cleanup;
}
- ret = vshSnapshotCreate(ctl, dom, buffer, 0, from);
+ ret = vshSnapshotCreate(ctl, dom, buffer, flags, from);
cleanup:
VIR_FREE(buffer);
@@ -12024,6 +12038,7 @@ static const vshCmdOptDef opts_snapshot_create_as[] = {
{"name", VSH_OT_DATA, 0, N_("name of snapshot")},
{"description", VSH_OT_DATA, 0, N_("description of snapshot")},
{"print-xml", VSH_OT_BOOL, 0, N_("print XML document rather than
create")},
+ {"no-metadata", VSH_OT_BOOL, 0, N_("take snapshot but create no
metadata")},
{NULL, 0, 0, NULL}
};
@@ -12036,6 +12051,10 @@ cmdSnapshotCreateAs(vshControl *ctl, const vshCmd *cmd)
const char *name = NULL;
const char *desc = NULL;
virBuffer buf = VIR_BUFFER_INITIALIZER;
+ unsigned int flags = 0;
+
+ if (vshCommandOptBool(cmd, "no-metadata"))
+ flags |= VIR_DOMAIN_SNAPSHOT_CREATE_NO_METADATA;
if (!vshConnectionUsability(ctl, ctl->conn))
goto cleanup;
@@ -12069,7 +12088,7 @@ cmdSnapshotCreateAs(vshControl *ctl, const vshCmd *cmd)
goto cleanup;
}
- ret = vshSnapshotCreate(ctl, dom, buffer, 0, NULL);
+ ret = vshSnapshotCreate(ctl, dom, buffer, flags, NULL);
cleanup:
VIR_FREE(buffer);
@@ -12080,11 +12099,110 @@ cleanup:
}
/*
+ * "snapshot-edit" command
+ */
+static const vshCmdInfo info_snapshot_edit[] = {
+ {"help", N_("edit XML for a snapshot")},
+ {"desc", N_("Edit the domain snapshot XML for a named
snapshot")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_snapshot_edit[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or
uuid")},
+ {"snapshotname", VSH_OT_DATA, VSH_OFLAG_REQ, N_("snapshot
name")},
+ {"current", VSH_OT_BOOL, 0, N_("also set edited snapshot as
current")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdSnapshotEdit(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom = NULL;
+ virDomainSnapshotPtr snapshot = NULL;
+ const char *name;
+ bool ret = false;
+ char *tmp = NULL;
+ char *doc = NULL;
+ char *doc_edited = NULL;
+ unsigned int getxml_flags = VIR_DOMAIN_XML_SECURE;
+ unsigned int define_flags = VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE;
+
+ if (vshCommandOptBool(cmd, "current"))
+ define_flags |= VIR_DOMAIN_SNAPSHOT_CREATE_CURRENT;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (vshCommandOptString(cmd, "snapshotname", &name) <= 0)
+ goto cleanup;
+
+ dom = vshCommandOptDomain(ctl, cmd, NULL);
+ if (dom == NULL)
+ goto cleanup;
+
+ snapshot = virDomainSnapshotLookupByName(dom, name, 0);
+ if (snapshot == NULL)
+ goto cleanup;
+
+ /* Get the XML configuration of the snapshot. */
+ doc = virDomainSnapshotGetXMLDesc(snapshot, getxml_flags);
+ if (!doc)
+ goto cleanup;
+
+ /* Create and open the temporary file. */
+ tmp = editWriteToTempFile(ctl, doc);
+ if (!tmp)
+ goto cleanup;
+
+ /* Start the editor. */
+ if (editFile(ctl, tmp) == -1)
+ goto cleanup;
+
+ /* Read back the edited file. */
+ doc_edited = editReadBackFile(ctl, tmp);
+ if (!doc_edited)
+ goto cleanup;
+
+ /* Compare original XML with edited. Short-circuit if it did not
+ * change, and we do not have any flags. */
+ if (STREQ(doc, doc_edited) &&
+ !(define_flags & VIR_DOMAIN_SNAPSHOT_CREATE_CURRENT)) {
+ vshPrint(ctl, _("Snapshot %s XML configuration not changed.\n"),
+ name);
+ ret = true;
+ goto cleanup;
+ }
+
+ /* Everything checks out, so redefine the xml. */
+ snapshot = virDomainSnapshotCreateXML(dom, doc_edited, define_flags);
+ if (!snapshot) {
+ vshError(ctl, _("Failed to update %s"), name);
+ goto cleanup;
+ }
+
+ vshPrint(ctl, _("Snapshot %s edited.\n"), name);
+ ret = true;
+
+cleanup:
+ VIR_FREE(doc);
+ VIR_FREE(doc_edited);
+ if (tmp) {
+ unlink(tmp);
+ VIR_FREE(tmp);
+ }
+ if (snapshot)
+ virDomainSnapshotFree(snapshot);
+ if (dom)
+ virDomainFree(dom);
+ return ret;
+}
+
+/*
* "snapshot-current" command
*/
static const vshCmdInfo info_snapshot_current[] = {
- {"help", N_("Get the current snapshot")},
- {"desc", N_("Get the current snapshot")},
+ {"help", N_("Get or set the current snapshot")},
+ {"desc", N_("Get or set the current snapshot")},
{NULL, NULL}
};
@@ -12093,6 +12211,8 @@ static const vshCmdOptDef opts_snapshot_current[] = {
{"name", VSH_OT_BOOL, 0, N_("list the name, rather than the full
xml")},
{"security-info", VSH_OT_BOOL, 0,
N_("include security sensitive information in XML dump")},
+ {"snapshotname", VSH_OT_DATA, 0,
+ N_("name of existing snapshot to make current")},
{NULL, 0, 0, NULL}
};
@@ -12104,6 +12224,7 @@ cmdSnapshotCurrent(vshControl *ctl, const vshCmd *cmd)
int current;
virDomainSnapshotPtr snapshot = NULL;
char *xml = NULL;
+ const char *snapshotname = NULL;
unsigned int flags = 0;
if (vshCommandOptBool(cmd, "security-info"))
@@ -12116,6 +12237,35 @@ cmdSnapshotCurrent(vshControl *ctl, const vshCmd *cmd)
if (dom == NULL)
goto cleanup;
+ if (vshCommandOptString(cmd, "snapshotname", &snapshotname) < 0) {
+ vshError(ctl, _("invalid snapshotname argument '%s'"),
snapshotname);
+ goto cleanup;
+ }
+ if (snapshotname) {
+ virDomainSnapshotPtr snapshot2 = NULL;
+ flags = (VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE |
+ VIR_DOMAIN_SNAPSHOT_CREATE_CURRENT);
+
+ if (vshCommandOptBool(cmd, "name")) {
+ vshError(ctl, "%s",
+ _("--name and snapshotname are mutually exclusive"));
+ goto cleanup;
+ }
+ snapshot = virDomainSnapshotLookupByName(dom, snapshotname, 0);
+ if (snapshot == NULL)
+ goto cleanup;
+ xml = virDomainSnapshotGetXMLDesc(snapshot, VIR_DOMAIN_XML_SECURE);
+ if (!xml)
+ goto cleanup;
+ snapshot2 = virDomainSnapshotCreateXML(dom, xml, flags);
+ if (snapshot2 == NULL)
+ goto cleanup;
+ virDomainSnapshotFree(snapshot2);
+ vshPrint(ctl, _("Snapshot %s set as current"), snapshotname);
+ ret = true;
+ goto cleanup;
+ }
+
current = virDomainHasCurrentSnapshot(dom, 0);
if (current < 0)
goto cleanup;
@@ -12950,6 +13100,8 @@ static const vshCmdDef snapshotCmds[] = {
info_snapshot_delete, 0},
{"snapshot-dumpxml", cmdSnapshotDumpXML, opts_snapshot_dumpxml,
info_snapshot_dumpxml, 0},
+ {"snapshot-edit", cmdSnapshotEdit, opts_snapshot_edit,
+ info_snapshot_edit, 0},
{"snapshot-list", cmdSnapshotList, opts_snapshot_list,
info_snapshot_list, 0},
{"snapshot-parent", cmdSnapshotParent, opts_snapshot_parent,
diff --git a/tools/virsh.pod b/tools/virsh.pod
index cfdfed9..2c8d66c 100644
--- a/tools/virsh.pod
+++ b/tools/virsh.pod
@@ -1675,15 +1675,33 @@ used to represent properties of snapshots.
=over 4
-=item B<snapshot-create> I<domain> [I<xmlfile>]
+=item B<snapshot-create> I<domain> [I<xmlfile>] {[I<--redefine>
[I<--current>]]
+| [I<--no-metadata>]}
Create a snapshot for domain I<domain> with the properties specified in
-I<xmlfile>. The only properties settable for a domain snapshot are the
-<name> and <description>; the rest of the fields are ignored, and
-automatically filled in by libvirt. If I<xmlfile> is completely omitted,
-then libvirt will choose a value for all fields.
-
-=item B<snapshot-create-as> I<domain> [I<--print-xml>]
+I<xmlfile>. Normally, the only properties settable for a domain snapshot
+are the <name> and <description> elements; the rest of the fields are
+ignored, and automatically filled in by libvirt. If I<xmlfile> is
+completely omitted, then libvirt will choose a value for all fields.
+The new snapshot will become current, as listed by B<snapshot-current>.
+
+If I<--redefine> is specified, then all XML elements produced by
+B<snapshot-dumpxml> are valid; this can be used to migrate snapshot
+hierarchy from one machine to another, to recreate hierarchy for the
+case of a transient domain that goes away and is later recreated with
+the same name and UUID, or to make slight alterations in the snapshot
+metadata (such as host-specific aspects of the domain XML embedded in
+the snapshot). When this flag is supplied, the I<xmlfile> argument
+is mandatory, and the domain's current snapshot will not be altered
+unless the I<--current> flag is also given.
+
+If I<--no-metadata> is specified, then the snapshot data is created,
+but any metadata is immediately discarded (that is, libvirt does not
+treat the snapshot as current, and cannot revert to the snapshot
+unless I<--redefine> is later used to teach libvirt about the
+metadata again).
+
+=item B<snapshot-create-as> I<domain> {[I<--print-xml>] |
[I<--no-metadata>]}
[I<name>] [I<description>]
Create a snapshot for domain I<domain> with the given <name> and
@@ -1691,13 +1709,40 @@ Create a snapshot for domain I<domain> with the given
<name> and
value. If I<--print-xml> is specified, then XML appropriate for
I<snapshot-create> is output, rather than actually creating a snapshot.
-=item B<snapshot-current> I<domain> [I<--name>]
-=item B<snapshot-current> I<domain> {[I<--name>] |
[I<--security-info]}
+If I<--no-metadata> is specified, then the snapshot data is created,
+but any metadata is immediately discarded (that is, libvirt does not
+treat the snapshot as current, and cannot revert to the snapshot
+unless B<snapshot-create> is later used to teach libvirt about the
+metadata again). This flag is incompatible with I<--print-xml>.
+
+=item B<snapshot-current> I<domain> {[I<--name>] |
[I<--security-info]
+| [I<snapshotname>]}
+
+Without I<snapshotname>, this will output the snapshot XML for the domain's
+current snapshot (if any). If I<--name> is specified, just the
+current snapshot name instead of the full xml. Otherwise, using
+I<--security-info> will also include security sensitive information in
+the XML.
+
+With I<snapshotname>, this is a request to make the existing named
+snapshot become the current snapshot, without reverting the domain.
-Output the snapshot XML for the domain's current snapshot (if any).
-If I<--name> is specified, just print the current snapshot name instead
-of the full xml. Otherwise, using I<--security-info> will also include
-security sensitive information in the XML.
+=item B<snapshot-edit> I<domain> I<snapshotname> [I<--current>]
+
+Edit the XML configuration file for I<snapshotname> of a domain. If
+I<--current> is specified, also force the edited snapshot to become
+the current snapshot.
+
+This is equivalent to:
+
+ virsh snapshot-dumpxml dom name > snapshot.xml
+ vi snapshot.xml (or make changes with your other text editor)
+ virsh snapshot-create dom snapshot.xml --redefine [--current]
+
+except that it does some error checking.
+
+The editor used can be supplied by the C<$VISUAL> or C<$EDITOR> environment
+variables, and defaults to C<vi>.
=item B<snapshot-list> I<domain> [{I<--parent> | I<--roots>}]
[I<--metadata>]
--
1.7.4.4