From 2b4d8deb6bbcdac0015607797bfebb1faf8a0245 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Thu, 1 Sep 2011 14:21:28 -0600 Subject: [PATCH] snapshot: improve virsh snapshot-create, add snapshot-edit 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 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 | 164 ++++++++++++++++++++++++++++++++++++++++++++++-- tools/virsh.pod | 69 ++++++++++++++++---- 2 files changed, 216 insertions(+), 17 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index 22a9d22205..fb6e5aed22 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -11931,7 +11931,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; @@ -11975,6 +11978,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} }; @@ -11985,6 +11991,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; @@ -12010,7 +12024,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); @@ -12034,6 +12048,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} }; @@ -12046,6 +12061,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; @@ -12079,7 +12098,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); @@ -12089,12 +12108,113 @@ cleanup: return ret; } +/* + * "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; + virDomainSnapshotFree(snapshot); + snapshot = NULL; + + /* 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} }; @@ -12103,6 +12223,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} }; @@ -12114,6 +12236,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")) @@ -12126,6 +12249,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; @@ -12960,6 +13112,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 3d468f321f..07439a88ba 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -1685,15 +1685,33 @@ used to represent properties of snapshots. =over 4 -=item B I [I] +=item B I [I] {[I<--redefine> [I<--current>]] +| [I<--no-metadata>]} Create a snapshot for domain I with the properties specified in -I. The only properties settable for a domain snapshot are the - and ; the rest of the fields are ignored, and -automatically filled in by libvirt. If I is completely omitted, -then libvirt will choose a value for all fields. +I. Normally, the only properties settable for a domain snapshot +are the and elements; the rest of the fields are +ignored, and automatically filled in by libvirt. If I is +completely omitted, then libvirt will choose a value for all fields. +The new snapshot will become current, as listed by B. -=item B I [I<--print-xml>] +If I<--redefine> is specified, then all XML elements produced by +B 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 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 I {[I<--print-xml>] | [I<--no-metadata>]} [I] [I] Create a snapshot for domain I with the given and @@ -1701,13 +1719,40 @@ Create a snapshot for domain I with the given and value. If I<--print-xml> is specified, then XML appropriate for I is output, rather than actually creating a snapshot. -=item B I [I<--name>] -=item B I {[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 is later used to teach libvirt about the +metadata again). This flag is incompatible with I<--print-xml>. -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 I {[I<--name>] | [I<--security-info] +| [I]} + +Without I, 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, this is a request to make the existing named +snapshot become the current snapshot, without reverting the domain. + +=item B I I [I<--current>] + +Edit the XML configuration file for I 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. =item B I [{I<--parent> | I<--roots>}] [I<--metadata>]