snapshot: wire up qemu transaction command

The hardest part about adding transactions is not using the new
monitor command, but undoing the partial changes we made prior
to a failed transaction.

* src/qemu/qemu_driver.c (qemuDomainSnapshotCreateDiskActive): Use
transaction when available.
(qemuDomainSnapshotUndoSingleDiskActive): New function.
(qemuDomainSnapshotCreateSingleDiskActive): Pass through actions.
(qemuDomainSnapshotCreateXML): Adjust caller.
This commit is contained in:
Eric Blake 2012-03-17 14:16:25 -06:00
parent 64d5e815b7
commit 0436d328f5

View File

@ -9823,7 +9823,8 @@ qemuDomainSnapshotCreateSingleDiskActive(struct qemud_driver *driver,
virDomainObjPtr vm, virDomainObjPtr vm,
virDomainSnapshotDiskDefPtr snap, virDomainSnapshotDiskDefPtr snap,
virDomainDiskDefPtr disk, virDomainDiskDefPtr disk,
virDomainDiskDefPtr persistDisk) virDomainDiskDefPtr persistDisk,
virJSONValuePtr actions)
{ {
qemuDomainObjPrivatePtr priv = vm->privateData; qemuDomainObjPrivatePtr priv = vm->privateData;
char *device = NULL; char *device = NULL;
@ -9883,7 +9884,7 @@ qemuDomainSnapshotCreateSingleDiskActive(struct qemud_driver *driver,
origdriver = NULL; origdriver = NULL;
/* create the actual snapshot */ /* create the actual snapshot */
ret = qemuMonitorDiskSnapshot(priv->mon, NULL, device, source); ret = qemuMonitorDiskSnapshot(priv->mon, actions, device, source);
virDomainAuditDisk(vm, disk->src, source, "snapshot", ret >= 0); virDomainAuditDisk(vm, disk->src, source, "snapshot", ret >= 0);
if (ret < 0) if (ret < 0)
goto cleanup; goto cleanup;
@ -9924,6 +9925,69 @@ cleanup:
return ret; return ret;
} }
/* The domain is expected to hold monitor lock. This is the
* counterpart to qemuDomainSnapshotCreateSingleDiskActive, called
* only on a failed transaction. */
static void
qemuDomainSnapshotUndoSingleDiskActive(struct qemud_driver *driver,
virDomainObjPtr vm,
virDomainDiskDefPtr origdisk,
virDomainDiskDefPtr disk,
virDomainDiskDefPtr persistDisk,
bool need_unlink)
{
char *source = NULL;
char *driverType = NULL;
char *persistSource = NULL;
char *persistDriverType = NULL;
struct stat st;
if (!(source = strdup(origdisk->src)) ||
(origdisk->driverType &&
!(driverType = strdup(origdisk->driverType))) ||
(persistDisk &&
(!(persistSource = strdup(source)) ||
(driverType && !(persistDriverType = strdup(driverType)))))) {
virReportOOMError();
goto cleanup;
}
if (virSecurityManagerRestoreImageLabel(driver->securityManager,
vm->def, disk) < 0)
VIR_WARN("Unable to restore security label on %s", disk->src);
if (virDomainLockDiskDetach(driver->lockManager, vm, disk) < 0)
VIR_WARN("Unable to release lock on %s", disk->src);
if (need_unlink && stat(disk->src, &st) == 0 &&
st.st_size == 0 && S_ISREG(st.st_mode) && unlink(disk->src) < 0)
VIR_WARN("Unable to remove just-created %s", disk->src);
/* Update vm in place to match changes. */
VIR_FREE(disk->src);
disk->src = source;
source = NULL;
VIR_FREE(disk->driverType);
if (driverType) {
disk->driverType = driverType;
driverType = NULL;
}
if (persistDisk) {
VIR_FREE(persistDisk->src);
persistDisk->src = persistSource;
persistSource = NULL;
VIR_FREE(persistDisk->driverType);
if (persistDriverType) {
persistDisk->driverType = persistDriverType;
persistDriverType = NULL;
}
}
cleanup:
VIR_FREE(source);
VIR_FREE(driverType);
VIR_FREE(persistSource);
VIR_FREE(persistDriverType);
}
/* The domain is expected to be locked and active. */ /* The domain is expected to be locked and active. */
static int static int
qemuDomainSnapshotCreateDiskActive(virConnectPtr conn, qemuDomainSnapshotCreateDiskActive(virConnectPtr conn,
@ -9933,6 +9997,8 @@ qemuDomainSnapshotCreateDiskActive(virConnectPtr conn,
unsigned int flags) unsigned int flags)
{ {
virDomainObjPtr vm = *vmptr; virDomainObjPtr vm = *vmptr;
qemuDomainObjPrivatePtr priv = vm->privateData;
virJSONValuePtr actions = NULL;
bool resume = false; bool resume = false;
int ret = -1; int ret = -1;
int i; int i;
@ -9982,9 +10048,17 @@ qemuDomainSnapshotCreateDiskActive(virConnectPtr conn,
goto cleanup; goto cleanup;
} }
} }
if (qemuCapsGet(priv->qemuCaps, QEMU_CAPS_TRANSACTION)) {
actions = virJSONValueNewArray();
if (!actions) {
virReportOOMError();
goto cleanup;
}
}
/* No way to roll back if first disk succeeds but later disks /* No way to roll back if first disk succeeds but later disks
* fail. Based on earlier qemuDomainSnapshotDiskPrepare, all * fail, unless we have transaction support.
* Based on earlier qemuDomainSnapshotDiskPrepare, all
* disks in this list are now either SNAPSHOT_NO, or * disks in this list are now either SNAPSHOT_NO, or
* SNAPSHOT_EXTERNAL with a valid file name and qcow2 format. */ * SNAPSHOT_EXTERNAL with a valid file name and qcow2 format. */
qemuDomainObjEnterMonitorWithDriver(driver, vm); qemuDomainObjEnterMonitorWithDriver(driver, vm);
@ -10006,10 +10080,37 @@ qemuDomainSnapshotCreateDiskActive(virConnectPtr conn,
ret = qemuDomainSnapshotCreateSingleDiskActive(driver, vm, ret = qemuDomainSnapshotCreateSingleDiskActive(driver, vm,
&snap->def->disks[i], &snap->def->disks[i],
vm->def->disks[i], vm->def->disks[i],
persistDisk); persistDisk, actions);
if (ret < 0) if (ret < 0)
break; break;
} }
if (actions) {
if (ret == 0)
ret = qemuMonitorTransaction(priv->mon, actions);
if (ret < 0) {
/* Transaction failed; undo the changes to vm. */
bool need_unlink = !(flags & VIR_DOMAIN_SNAPSHOT_CREATE_REUSE_EXT);
while (--i >= 0) {
virDomainDiskDefPtr persistDisk = NULL;
if (snap->def->disks[i].snapshot == VIR_DOMAIN_DISK_SNAPSHOT_NO)
continue;
if (vm->newDef) {
int indx = virDomainDiskIndexByName(vm->newDef,
vm->def->disks[i]->dst,
false);
if (indx >= 0)
persistDisk = vm->newDef->disks[indx];
}
qemuDomainSnapshotUndoSingleDiskActive(driver, vm,
snap->def->dom->disks[i],
vm->def->disks[i],
persistDisk,
need_unlink);
}
}
}
qemuDomainObjExitMonitorWithDriver(driver, vm); qemuDomainObjExitMonitorWithDriver(driver, vm);
if (ret < 0) if (ret < 0)
goto cleanup; goto cleanup;
@ -10043,7 +10144,8 @@ cleanup:
} }
} }
if (vm) { if (vm && (ret == 0 ||
!qemuCapsGet(priv->qemuCaps, QEMU_CAPS_TRANSACTION))) {
if (virDomainSaveStatus(driver->caps, driver->stateDir, vm) < 0 || if (virDomainSaveStatus(driver->caps, driver->stateDir, vm) < 0 ||
(persist && (persist &&
virDomainSaveConfig(driver->configDir, vm->newDef) < 0)) virDomainSaveConfig(driver->configDir, vm->newDef) < 0))