mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-09-09 01:04:59 +00:00
snapshot: track current snapshot across restarts
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. This patch does not fix the case of virDomainSnapshotDelete(,CHILDREN) where one of the children is the current snapshot; that will be later. * 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.
This commit is contained in:
parent
5e47785b85
commit
6766ff10dd
@ -29,7 +29,10 @@
|
|||||||
</optional>
|
</optional>
|
||||||
<optional>
|
<optional>
|
||||||
<element name='active'>
|
<element name='active'>
|
||||||
<text/>
|
<choice>
|
||||||
|
<value>0</value>
|
||||||
|
<value>1</value>
|
||||||
|
</choice>
|
||||||
</element>
|
</element>
|
||||||
</optional>
|
</optional>
|
||||||
<optional>
|
<optional>
|
||||||
|
@ -10960,6 +10960,7 @@ virDomainSnapshotDefPtr virDomainSnapshotDefParseString(const char *xmlStr,
|
|||||||
virDomainSnapshotDefPtr ret = NULL;
|
virDomainSnapshotDefPtr ret = NULL;
|
||||||
char *creation = NULL, *state = NULL;
|
char *creation = NULL, *state = NULL;
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
|
int active;
|
||||||
|
|
||||||
xml = virXMLParseCtxt(NULL, xmlStr, "domainsnapshot.xml", &ctxt);
|
xml = virXMLParseCtxt(NULL, xmlStr, "domainsnapshot.xml", &ctxt);
|
||||||
if (!xml) {
|
if (!xml) {
|
||||||
@ -11016,11 +11017,12 @@ virDomainSnapshotDefPtr virDomainSnapshotDefParseString(const char *xmlStr,
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (virXPathLong("string(./active)", ctxt, &def->active) < 0) {
|
if (virXPathInt("string(./active)", ctxt, &active) < 0) {
|
||||||
virDomainReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
virDomainReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||||
_("Could not find 'active' element"));
|
_("Could not find 'active' element"));
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
def->current = active != 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
def->creationTime = tv.tv_sec;
|
def->creationTime = tv.tv_sec;
|
||||||
@ -11062,7 +11064,7 @@ char *virDomainSnapshotDefFormat(char *domain_uuid,
|
|||||||
virBufferAsprintf(&buf, " <uuid>%s</uuid>\n", domain_uuid);
|
virBufferAsprintf(&buf, " <uuid>%s</uuid>\n", domain_uuid);
|
||||||
virBufferAddLit(&buf, " </domain>\n");
|
virBufferAddLit(&buf, " </domain>\n");
|
||||||
if (internal)
|
if (internal)
|
||||||
virBufferAsprintf(&buf, " <active>%ld</active>\n", def->active);
|
virBufferAsprintf(&buf, " <active>%d</active>\n", def->current);
|
||||||
virBufferAddLit(&buf, "</domainsnapshot>\n");
|
virBufferAddLit(&buf, "</domainsnapshot>\n");
|
||||||
|
|
||||||
if (virBufferError(&buf)) {
|
if (virBufferError(&buf)) {
|
||||||
|
@ -1297,13 +1297,15 @@ enum virDomainTaintFlags {
|
|||||||
typedef struct _virDomainSnapshotDef virDomainSnapshotDef;
|
typedef struct _virDomainSnapshotDef virDomainSnapshotDef;
|
||||||
typedef virDomainSnapshotDef *virDomainSnapshotDefPtr;
|
typedef virDomainSnapshotDef *virDomainSnapshotDefPtr;
|
||||||
struct _virDomainSnapshotDef {
|
struct _virDomainSnapshotDef {
|
||||||
|
/* Public XML. */
|
||||||
char *name;
|
char *name;
|
||||||
char *description;
|
char *description;
|
||||||
char *parent;
|
char *parent;
|
||||||
long long creationTime; /* in seconds */
|
long long creationTime; /* in seconds */
|
||||||
int state;
|
int state;
|
||||||
|
|
||||||
long active;
|
/* Internal use. */
|
||||||
|
bool current; /* At most one snapshot in the list should have this set */
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct _virDomainSnapshotObj virDomainSnapshotObj;
|
typedef struct _virDomainSnapshotObj virDomainSnapshotObj;
|
||||||
|
@ -294,6 +294,7 @@ static void qemuDomainSnapshotLoad(void *payload,
|
|||||||
char *fullpath;
|
char *fullpath;
|
||||||
virDomainSnapshotDefPtr def = NULL;
|
virDomainSnapshotDefPtr def = NULL;
|
||||||
virDomainSnapshotObjPtr snap = NULL;
|
virDomainSnapshotObjPtr snap = NULL;
|
||||||
|
virDomainSnapshotObjPtr current = NULL;
|
||||||
char ebuf[1024];
|
char ebuf[1024];
|
||||||
|
|
||||||
virDomainObjLock(vm);
|
virDomainObjLock(vm);
|
||||||
@ -339,7 +340,8 @@ static void qemuDomainSnapshotLoad(void *payload,
|
|||||||
def = virDomainSnapshotDefParseString(xmlStr, 0);
|
def = virDomainSnapshotDefParseString(xmlStr, 0);
|
||||||
if (def == NULL) {
|
if (def == NULL) {
|
||||||
/* Nothing we can do here, skip this one */
|
/* 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(fullpath);
|
||||||
VIR_FREE(xmlStr);
|
VIR_FREE(xmlStr);
|
||||||
continue;
|
continue;
|
||||||
@ -348,12 +350,22 @@ static void qemuDomainSnapshotLoad(void *payload,
|
|||||||
snap = virDomainSnapshotAssignDef(&vm->snapshots, def);
|
snap = virDomainSnapshotAssignDef(&vm->snapshots, def);
|
||||||
if (snap == NULL) {
|
if (snap == NULL) {
|
||||||
virDomainSnapshotDefFree(def);
|
virDomainSnapshotDefFree(def);
|
||||||
|
} else if (snap->def->current) {
|
||||||
|
current = snap;
|
||||||
|
if (!vm->current_snapshot)
|
||||||
|
vm->current_snapshot = snap;
|
||||||
}
|
}
|
||||||
|
|
||||||
VIR_FREE(fullpath);
|
VIR_FREE(fullpath);
|
||||||
VIR_FREE(xmlStr);
|
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
|
/* FIXME: qemu keeps internal track of snapshots. We can get access
|
||||||
* to this info via the "info snapshots" monitor command for running
|
* to this info via the "info snapshots" monitor command for running
|
||||||
* domains, or via "qemu-img snapshot -l" for shutoff domains. It would
|
* domains, or via "qemu-img snapshot -l" for shutoff domains. It would
|
||||||
@ -8477,12 +8489,17 @@ static virDomainSnapshotPtr qemuDomainSnapshotCreateXML(virDomainPtr domain,
|
|||||||
def = NULL;
|
def = NULL;
|
||||||
|
|
||||||
snap->def->state = virDomainObjGetState(vm, NULL);
|
snap->def->state = virDomainObjGetState(vm, NULL);
|
||||||
|
snap->def->current = true;
|
||||||
if (vm->current_snapshot) {
|
if (vm->current_snapshot) {
|
||||||
snap->def->parent = strdup(vm->current_snapshot->def->name);
|
snap->def->parent = strdup(vm->current_snapshot->def->name);
|
||||||
if (snap->def->parent == NULL) {
|
if (snap->def->parent == NULL) {
|
||||||
virReportOOMError();
|
virReportOOMError();
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
vm->current_snapshot->def->current = false;
|
||||||
|
if (qemuDomainSnapshotWriteMetadata(vm, vm->current_snapshot,
|
||||||
|
driver->snapshotDir) < 0)
|
||||||
|
goto cleanup;
|
||||||
vm->current_snapshot = NULL;
|
vm->current_snapshot = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -8502,6 +8519,7 @@ static virDomainSnapshotPtr qemuDomainSnapshotCreateXML(virDomainPtr domain,
|
|||||||
*/
|
*/
|
||||||
if (qemuDomainSnapshotWriteMetadata(vm, snap, driver->snapshotDir) < 0)
|
if (qemuDomainSnapshotWriteMetadata(vm, snap, driver->snapshotDir) < 0)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
vm->current_snapshot = snap;
|
||||||
|
|
||||||
snapshot = virGetDomainSnapshot(domain, snap->def->name);
|
snapshot = virGetDomainSnapshot(domain, snap->def->name);
|
||||||
|
|
||||||
@ -8742,7 +8760,17 @@ static int qemuDomainRevertToSnapshot(virDomainSnapshotPtr snapshot,
|
|||||||
goto cleanup;
|
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)
|
if (qemuDomainObjBeginJobWithDriver(driver, vm, QEMU_JOB_MODIFY) < 0)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
@ -8759,7 +8787,7 @@ static int qemuDomainRevertToSnapshot(virDomainSnapshotPtr snapshot,
|
|||||||
goto endjob;
|
goto endjob;
|
||||||
} else {
|
} else {
|
||||||
rc = qemuProcessStart(snapshot->domain->conn, driver, vm, NULL,
|
rc = qemuProcessStart(snapshot->domain->conn, driver, vm, NULL,
|
||||||
false, false, -1, NULL, vm->current_snapshot,
|
false, false, -1, NULL, snap,
|
||||||
VIR_VM_OP_CREATE);
|
VIR_VM_OP_CREATE);
|
||||||
virDomainAuditStart(vm, "from-snapshot", rc >= 0);
|
virDomainAuditStart(vm, "from-snapshot", rc >= 0);
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
@ -8817,6 +8845,15 @@ endjob:
|
|||||||
vm = NULL;
|
vm = NULL;
|
||||||
|
|
||||||
cleanup:
|
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)
|
if (event)
|
||||||
qemuDomainEventQueue(driver, event);
|
qemuDomainEventQueue(driver, event);
|
||||||
if (vm)
|
if (vm)
|
||||||
@ -8835,7 +8872,7 @@ static int qemuDomainSnapshotDiscard(struct qemud_driver *driver,
|
|||||||
int ret = -1;
|
int ret = -1;
|
||||||
int i;
|
int i;
|
||||||
qemuDomainObjPrivatePtr priv;
|
qemuDomainObjPrivatePtr priv;
|
||||||
virDomainSnapshotObjPtr parentsnap;
|
virDomainSnapshotObjPtr parentsnap = NULL;
|
||||||
|
|
||||||
if (!virDomainObjIsActive(vm)) {
|
if (!virDomainObjIsActive(vm)) {
|
||||||
qemuimgarg[0] = qemuFindQemuImgBinary();
|
qemuimgarg[0] = qemuFindQemuImgBinary();
|
||||||
@ -8874,31 +8911,35 @@ static int qemuDomainSnapshotDiscard(struct qemud_driver *driver,
|
|||||||
qemuDomainObjExitMonitorWithDriver(driver, vm);
|
qemuDomainObjExitMonitorWithDriver(driver, vm);
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Now we set the new current_snapshot for the domain */
|
|
||||||
vm->current_snapshot = parentsnap;
|
|
||||||
} else {
|
|
||||||
vm->current_snapshot = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (virAsprintf(&snapFile, "%s/%s/%s.xml", driver->snapshotDir,
|
if (virAsprintf(&snapFile, "%s/%s/%s.xml", driver->snapshotDir,
|
||||||
vm->def->name, snap->def->name) < 0) {
|
vm->def->name, snap->def->name) < 0) {
|
||||||
virReportOOMError();
|
virReportOOMError();
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
unlink(snapFile);
|
|
||||||
|
|
||||||
|
if (snap == vm->current_snapshot) {
|
||||||
|
if (snap->def->parent) {
|
||||||
|
parentsnap = virDomainSnapshotFindByName(&vm->snapshots,
|
||||||
|
snap->def->parent);
|
||||||
|
if (!parentsnap) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vm->current_snapshot = parentsnap;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unlink(snapFile) < 0)
|
||||||
|
VIR_WARN("Failed to unlink %s", snapFile);
|
||||||
virDomainSnapshotObjListRemove(&vm->snapshots, snap);
|
virDomainSnapshotObjListRemove(&vm->snapshots, snap);
|
||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
Loading…
Reference in New Issue
Block a user