Add a new function to make it possible to parse a list of snapshots
at once. This is a counterpart to an earlier patch making it
possible to produce all snapshots in a single XML string, and
intentionally parses the same top-level element <snapshots> with
an optional attribute current='name'.
Note that since we know we started with no relations at all, and
since checking parent relationships per-snapshot is not viable as
we don't control which order the snapshots appear in, that we are
fine with doing a final pass to update all parent/child
relationships among the definitions.
Signed-off-by: Eric Blake <eblake(a)redhat.com>
---
src/conf/snapshot_conf.h | 7 +++
src/conf/snapshot_conf.c | 111 +++++++++++++++++++++++++++++++++++++++
src/libvirt_private.syms | 1 +
3 files changed, 119 insertions(+)
diff --git a/src/conf/snapshot_conf.h b/src/conf/snapshot_conf.h
index 69a7750b0b..f8af991907 100644
--- a/src/conf/snapshot_conf.h
+++ b/src/conf/snapshot_conf.h
@@ -132,6 +132,13 @@ virDomainSnapshotDefPtr virDomainSnapshotDefParseNode(xmlDocPtr xml,
virCapsPtr caps,
virDomainXMLOptionPtr xmlopt,
unsigned int flags);
+int virDomainSnapshotObjListParse(const char *xmlStr,
+ const unsigned char *domain_uuid,
+ virDomainSnapshotObjListPtr snapshots,
+ virDomainSnapshotObjPtr *current_snap,
+ virCapsPtr caps,
+ virDomainXMLOptionPtr xmlopt,
+ unsigned int flags);
void virDomainSnapshotDefFree(virDomainSnapshotDefPtr def);
char *virDomainSnapshotDefFormat(const char *uuidstr,
virDomainSnapshotDefPtr def,
diff --git a/src/conf/snapshot_conf.c b/src/conf/snapshot_conf.c
index a5b05eadf4..52742d82d6 100644
--- a/src/conf/snapshot_conf.c
+++ b/src/conf/snapshot_conf.c
@@ -507,6 +507,117 @@ virDomainSnapshotRedefineValidate(virDomainSnapshotDefPtr def,
}
+/* Parse a <snapshots> XML entry into snapshots, which must start empty.
+ * Any <domain> sub-elements of a <domainsnapshot> must match domain_uuid.
+ */
+int
+virDomainSnapshotObjListParse(const char *xmlStr,
+ const unsigned char *domain_uuid,
+ virDomainSnapshotObjListPtr snapshots,
+ virDomainSnapshotObjPtr *current_snap,
+ virCapsPtr caps,
+ virDomainXMLOptionPtr xmlopt,
+ unsigned int flags)
+{
+ int ret = -1;
+ xmlDocPtr xml;
+ xmlNodePtr root;
+ xmlXPathContextPtr ctxt = NULL;
+ int n;
+ size_t i;
+ int keepBlanksDefault = xmlKeepBlanksDefault(0);
+ VIR_AUTOFREE(xmlNodePtr *) nodes = NULL;
+ VIR_AUTOFREE(char *) current = NULL;
+
+ if (!(flags & VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE) ||
+ (flags & VIR_DOMAIN_SNAPSHOT_PARSE_INTERNAL)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("incorrect flags for bulk parse"));
+ return -1;
+ }
+ if (snapshots->metaroot.nchildren || *current_snap) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("bulk define of snapshots only possible with "
+ "no existing snapshot"));
+ return -1;
+ }
+
+ if (!(xml = virXMLParse(NULL, xmlStr, _("(domain_snapshot)"))))
+ goto cleanup;
+
+ root = xmlDocGetRootElement(xml);
+ if (!virXMLNodeNameEqual(root, "snapshots")) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("unexpected root element <%s>, "
+ "expecting <snapshots>"), root->name);
+ goto cleanup;
+ }
+ ctxt = xmlXPathNewContext(xml);
+ if (ctxt == NULL) {
+ virReportOOMError();
+ goto cleanup;
+ }
+ ctxt->node = root;
+ current = virXMLPropString(root, "current");
+
+ if ((n = virXPathNodeSet("./domainsnapshot", ctxt, &nodes)) < 0)
+ goto cleanup;
+ if (!n) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("expected at least one <domainsnapshot>
child"));
+ goto cleanup;
+ }
+
+ for (i = 0; i < n; i++) {
+ virDomainSnapshotDefPtr def;
+ virDomainSnapshotObjPtr snap;
+
+ def = virDomainSnapshotDefParseNode(xml, nodes[i], caps, xmlopt, flags);
+ if (!def)
+ goto cleanup;
+ if (!(snap = virDomainSnapshotAssignDef(snapshots, def))) {
+ virDomainSnapshotDefFree(def);
+ goto cleanup;
+ }
+ if (virDomainSnapshotRedefineValidate(def, domain_uuid, NULL, NULL,
+ flags) < 0)
+ goto cleanup;
+ }
+
+ if (virDomainSnapshotUpdateRelations(snapshots) < 0) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("<snapshots> contains inconsistent parent-child
"
+ "relationships"));
+ goto cleanup;
+ }
+
+ if (current) {
+ if (!(*current_snap = virDomainSnapshotFindByName(snapshots,
+ current))) {
+ virReportError(VIR_ERR_NO_DOMAIN_SNAPSHOT,
+ _("no snapshot matching current='%s'"),
current);
+ goto cleanup;
+ }
+ (*current_snap)->def->current = true;
+ }
+
+ ret = 0;
+ cleanup:
+ if (ret < 0) {
+ /* There were no snapshots before this call; so on error, just
+ * blindly delete anything created before the failure. */
+ virHashRemoveAll(snapshots->objs);
+ snapshots->metaroot.nchildren = 0;
+ snapshots->metaroot.first_child = NULL;
+ }
+ VIR_FREE(current);
+ xmlXPathFreeContext(ctxt);
+ xmlFreeDoc(xml);
+ xmlKeepBlanksDefault(keepBlanksDefault);
+ return ret;
+}
+
+
/**
* virDomainSnapshotDefAssignExternalNames:
* @def: snapshot def object
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 35e0c6d9dc..395e1f8764 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -899,6 +899,7 @@ virDomainSnapshotObjListFree;
virDomainSnapshotObjListGetNames;
virDomainSnapshotObjListNew;
virDomainSnapshotObjListNum;
+virDomainSnapshotObjListParse;
virDomainSnapshotObjListRemove;
virDomainSnapshotRedefinePrep;
virDomainSnapshotStateTypeFromString;
--
2.20.1