diff --git a/tools/virsh.c b/tools/virsh.c
index 75fb142034336028b913b21444d3a7a06937fc80..a719aabfc5de0b0e86b10a600e6284f56760744e 100644
--- a/tools/virsh.c
+++ b/tools/virsh.c
@@ -11129,6 +11129,106 @@ cleanup:
return ret;
}
+/*
+ * "snapshot-create-as" command
+ */
+static const vshCmdInfo info_snapshot_create_as[] = {
+ {"help", N_("Create a snapshot from a set of args")},
+ {"desc", N_("Create a snapshot (disk and RAM) from arguments")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_snapshot_create_as[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {"name", VSH_OT_DATA, 0, N_("name of snapshot")},
+ {"description", VSH_OT_DATA, 0, N_("description of snapshot")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdSnapshotCreateAs(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom = NULL;
+ bool ret = false;
+ char *buffer = NULL;
+ virDomainSnapshotPtr snapshot = NULL;
+ xmlDocPtr xml = NULL;
+ xmlXPathContextPtr ctxt = NULL;
+ char *doc = NULL;
+ const char *name = NULL;
+ const char *desc = NULL;
+ char *parsed_name = NULL;
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ goto cleanup;
+
+ dom = vshCommandOptDomain(ctl, cmd, NULL);
+ if (dom == NULL)
+ goto cleanup;
+
+ if (vshCommandOptString(cmd, "name", &name) < 0 ||
+ vshCommandOptString(cmd, "description", &desc) < 0) {
+ vshError(ctl, _("argument must not be empty"));
+ goto cleanup;
+ }
+
+ virBufferAddLit(&buf, "\n");
+ if (name)
+ virBufferAsprintf(&buf, " %s\n", name);
+ if (desc)
+ virBufferAsprintf(&buf, " %s\n", desc);
+ virBufferAddLit(&buf, "\n");
+
+ buffer = virBufferContentAndReset(&buf);
+ if (buffer == NULL) {
+ vshError(ctl, "%s", _("Out of memory"));
+ goto cleanup;
+ }
+
+ snapshot = virDomainSnapshotCreateXML(dom, buffer, 0);
+ if (snapshot == NULL)
+ goto cleanup;
+
+ doc = virDomainSnapshotGetXMLDesc(snapshot, 0);
+ if (!doc)
+ goto cleanup;
+
+ xml = xmlReadDoc((const xmlChar *) doc, "domainsnapshot.xml", NULL,
+ XML_PARSE_NOENT | XML_PARSE_NONET |
+ XML_PARSE_NOWARNING);
+ if (!xml)
+ goto cleanup;
+ ctxt = xmlXPathNewContext(xml);
+ if (!ctxt)
+ goto cleanup;
+
+ parsed_name = virXPathString("string(/domainsnapshot/name)", ctxt);
+ if (!parsed_name) {
+ vshError(ctl, "%s",
+ _("Could not find 'name' element in domain snapshot XML"));
+ goto cleanup;
+ }
+
+ vshPrint(ctl, _("Domain snapshot %s created\n"), name ? name : parsed_name);
+
+ ret = true;
+
+cleanup:
+ VIR_FREE(parsed_name);
+ xmlXPathFreeContext(ctxt);
+ if (xml)
+ xmlFreeDoc(xml);
+ if (snapshot)
+ virDomainSnapshotFree(snapshot);
+ VIR_FREE(doc);
+ VIR_FREE(buffer);
+ if (dom)
+ virDomainFree(dom);
+
+ return ret;
+}
+
/*
* "snapshot-current" command
*/
@@ -11768,6 +11868,8 @@ static const vshCmdDef virshCmds[] = {
static const vshCmdDef snapshotCmds[] = {
{"snapshot-create", cmdSnapshotCreate, opts_snapshot_create,
info_snapshot_create, 0},
+ {"snapshot-create-as", cmdSnapshotCreateAs, opts_snapshot_create_as,
+ info_snapshot_create_as, 0},
{"snapshot-current", cmdSnapshotCurrent, opts_snapshot_current,
info_snapshot_current, 0},
{"snapshot-delete", cmdSnapshotDelete, opts_snapshot_delete,
diff --git a/tools/virsh.pod b/tools/virsh.pod
index 93bff1ebbc6bcb87a421f76426ad905e2a2f5986..b9c54de80cad7f4aa703be22ac106ce738e25f8d 100644
--- a/tools/virsh.pod
+++ b/tools/virsh.pod
@@ -1414,7 +1414,7 @@ used to represent properties of snapshots.
=over 4
-=item B I I
+=item B I optional I
Create a snapshot for domain I with the properties specified in
I. The only properties settable for a domain snapshot are the
@@ -1422,6 +1422,12 @@ I. The only properties settable for a domain snapshot are the
automatically filled in by libvirt. If I is completely omitted,
then libvirt will choose a value for all fields.
+=item B I optional I I
+
+Create a snapshot for domain I with the given and
+; if either value is omitted, libvirt will choose a
+value.
+
=item B I
Output the snapshot XML for the domain's current snapshot (if any).