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>]