Audit all changes to the qemu vm->current_snapshot, and make them update the saved xml file for both the previous and the new snapshot, so that there is always at most one snapshot with <active>1</active> in the xml, and that snapshot is used as the current snapshot even across libvirtd restarts. * src/conf/domain_conf.h (_virDomainSnapshotDef): Alter member type and name. * src/conf/domain_conf.c (virDomainSnapshotDefParseString) (virDomainSnapshotDefFormat): Update clients. * docs/schemas/domainsnapshot.rng: Tighten rng. * src/qemu/qemu_driver.c (qemuDomainSnapshotLoad): Reload current snapshot. (qemuDomainSnapshotCreateXML, qemuDomainRevertToSnapshot) (qemuDomainSnapshotDiscard): Track current snapshot. --- docs/schemas/domainsnapshot.rng | 5 ++- src/conf/domain_conf.c | 6 ++- src/conf/domain_conf.h | 4 +- src/qemu/qemu_driver.c | 81 +++++++++++++++++++++++++++++---------- 4 files changed, 72 insertions(+), 24 deletions(-) diff --git a/docs/schemas/domainsnapshot.rng b/docs/schemas/domainsnapshot.rng index 86bab0b..410833f 100644 --- a/docs/schemas/domainsnapshot.rng +++ b/docs/schemas/domainsnapshot.rng @@ -29,7 +29,10 @@ </optional> <optional> <element name='active'> - <text/> + <choice> + <value>0</value> + <value>1</value> + </choice> </element> </optional> <optional> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 0a2c9eb..44212cf 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -10960,6 +10960,7 @@ virDomainSnapshotDefPtr virDomainSnapshotDefParseString(const char *xmlStr, virDomainSnapshotDefPtr ret = NULL; char *creation = NULL, *state = NULL; struct timeval tv; + int active; xml = virXMLParseCtxt(NULL, xmlStr, "domainsnapshot.xml", &ctxt); if (!xml) { @@ -11016,11 +11017,12 @@ virDomainSnapshotDefPtr virDomainSnapshotDefParseString(const char *xmlStr, goto cleanup; } - if (virXPathLong("string(./active)", ctxt, &def->active) < 0) { + if (virXPathInt("string(./active)", ctxt, &active) < 0) { virDomainReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not find 'active' element")); goto cleanup; } + def->current = active != 0; } else def->creationTime = tv.tv_sec; @@ -11062,7 +11064,7 @@ char *virDomainSnapshotDefFormat(char *domain_uuid, virBufferAsprintf(&buf, " <uuid>%s</uuid>\n", domain_uuid); virBufferAddLit(&buf, " </domain>\n"); if (internal) - virBufferAsprintf(&buf, " <active>%ld</active>\n", def->active); + virBufferAsprintf(&buf, " <active>%d</active>\n", def->current); virBufferAddLit(&buf, "</domainsnapshot>\n"); if (virBufferError(&buf)) { diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 2cc9b06..8382d28 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1297,13 +1297,15 @@ enum virDomainTaintFlags { typedef struct _virDomainSnapshotDef virDomainSnapshotDef; typedef virDomainSnapshotDef *virDomainSnapshotDefPtr; struct _virDomainSnapshotDef { + /* Public XML. */ char *name; char *description; char *parent; long long creationTime; /* in seconds */ int state; - long active; + /* Internal use. */ + bool current; /* At most one snapshot in the list should have this set */ }; typedef struct _virDomainSnapshotObj virDomainSnapshotObj; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index c4183de..b492c97 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -294,6 +294,7 @@ static void qemuDomainSnapshotLoad(void *payload, char *fullpath; virDomainSnapshotDefPtr def = NULL; virDomainSnapshotObjPtr snap = NULL; + virDomainSnapshotObjPtr current = NULL; char ebuf[1024]; virDomainObjLock(vm); @@ -339,7 +340,8 @@ static void qemuDomainSnapshotLoad(void *payload, def = virDomainSnapshotDefParseString(xmlStr, 0); if (def == NULL) { /* Nothing we can do here, skip this one */ - VIR_ERROR(_("Failed to parse snapshot XML from file '%s'"), fullpath); + VIR_ERROR(_("Failed to parse snapshot XML from file '%s'"), + fullpath); VIR_FREE(fullpath); VIR_FREE(xmlStr); continue; @@ -348,12 +350,22 @@ static void qemuDomainSnapshotLoad(void *payload, snap = virDomainSnapshotAssignDef(&vm->snapshots, def); if (snap == NULL) { virDomainSnapshotDefFree(def); + } else if (snap->def->current) { + current = snap; + if (!vm->current_snapshot) + vm->current_snapshot = snap; } VIR_FREE(fullpath); VIR_FREE(xmlStr); } + if (vm->current_snapshot != current) { + VIR_ERROR(_("Too many snapshots claiming to be current for domain %s"), + vm->def->name); + vm->current_snapshot = NULL; + } + /* FIXME: qemu keeps internal track of snapshots. We can get access * to this info via the "info snapshots" monitor command for running * domains, or via "qemu-img snapshot -l" for shutoff domains. It would @@ -8477,12 +8489,17 @@ static virDomainSnapshotPtr qemuDomainSnapshotCreateXML(virDomainPtr domain, def = NULL; snap->def->state = virDomainObjGetState(vm, NULL); + snap->def->current = true; if (vm->current_snapshot) { snap->def->parent = strdup(vm->current_snapshot->def->name); if (snap->def->parent == NULL) { virReportOOMError(); goto cleanup; } + vm->current_snapshot->def->current = false; + if (qemuDomainSnapshotWriteMetadata(vm, vm->current_snapshot, + driver->snapshotDir) < 0) + goto cleanup; vm->current_snapshot = NULL; } @@ -8502,6 +8519,7 @@ static virDomainSnapshotPtr qemuDomainSnapshotCreateXML(virDomainPtr domain, */ if (qemuDomainSnapshotWriteMetadata(vm, snap, driver->snapshotDir) < 0) goto cleanup; + vm->current_snapshot = snap; snapshot = virGetDomainSnapshot(domain, snap->def->name); @@ -8788,7 +8806,17 @@ static int qemuDomainRevertToSnapshot(virDomainSnapshotPtr snapshot, goto cleanup; } - vm->current_snapshot = snap; + if (vm->current_snapshot) { + vm->current_snapshot->def->current = false; + if (qemuDomainSnapshotWriteMetadata(vm, vm->current_snapshot, + driver->snapshotDir) < 0) + goto cleanup; + vm->current_snapshot = NULL; + /* XXX Should we restore vm->current_snapshot after this point + * in the failure cases where we know there was no change? */ + } + + snap->def->current = true; if (qemuDomainObjBeginJobWithDriver(driver, vm, QEMU_JOB_MODIFY) < 0) goto cleanup; @@ -8830,7 +8858,7 @@ static int qemuDomainRevertToSnapshot(virDomainSnapshotPtr snapshot, } } else { rc = qemuProcessStart(snapshot->domain->conn, driver, vm, NULL, - true, false, -1, NULL, vm->current_snapshot, + true, false, -1, NULL, snap, VIR_VM_OP_CREATE); virDomainAuditStart(vm, "from-snapshot", rc >= 0); if (rc < 0) @@ -8894,6 +8922,15 @@ endjob: vm = NULL; cleanup: + if (vm && ret == 0) { + if (qemuDomainSnapshotWriteMetadata(vm, snap, + driver->snapshotDir) < 0) + ret = -1; + else + vm->current_snapshot = snap; + } else if (snap) { + snap->def->current = false; + } if (event) qemuDomainEventQueue(driver, event); if (vm) @@ -8912,7 +8949,7 @@ static int qemuDomainSnapshotDiscard(struct qemud_driver *driver, int ret = -1; int i; qemuDomainObjPrivatePtr priv; - virDomainSnapshotObjPtr parentsnap; + virDomainSnapshotObjPtr parentsnap = NULL; if (!virDomainObjIsActive(vm)) { qemuimgarg[0] = qemuFindQemuImgBinary(); @@ -8951,31 +8988,35 @@ static int qemuDomainSnapshotDiscard(struct qemud_driver *driver, qemuDomainObjExitMonitorWithDriver(driver, vm); } + if (virAsprintf(&snapFile, "%s/%s/%s.xml", driver->snapshotDir, + vm->def->name, snap->def->name) < 0) { + virReportOOMError(); + goto cleanup; + } + if (snap == vm->current_snapshot) { if (snap->def->parent) { parentsnap = virDomainSnapshotFindByName(&vm->snapshots, snap->def->parent); if (!parentsnap) { - qemuReportError(VIR_ERR_NO_DOMAIN_SNAPSHOT, - _("no domain snapshot parent with matching name '%s'"), - snap->def->parent); - goto cleanup; + VIR_WARN("missing parent snapshot matching name '%s'", + snap->def->parent); + } else { + parentsnap->def->current = true; + if (qemuDomainSnapshotWriteMetadata(vm, parentsnap, + driver->snapshotDir) < 0) { + VIR_WARN("failed to set parent snapshot '%s' as current", + snap->def->parent); + parentsnap->def->current = false; + parentsnap = NULL; + } } - - /* Now we set the new current_snapshot for the domain */ - vm->current_snapshot = parentsnap; - } else { - vm->current_snapshot = NULL; } + vm->current_snapshot = parentsnap; } - if (virAsprintf(&snapFile, "%s/%s/%s.xml", driver->snapshotDir, - vm->def->name, snap->def->name) < 0) { - virReportOOMError(); - goto cleanup; - } - unlink(snapFile); - + if (unlink(snapFile) < 0) + VIR_WARN("Failed to unlink %s", snapFile); virDomainSnapshotObjListRemove(&vm->snapshots, snap); ret = 0; -- 1.7.4.4 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list