From: Masayoshi Mizuma <m.mizuma@xxxxxxxxxxxxxx> Here is the implementation of transient option for qcow2 and raw format disk. This gets available <transient/> directive in domain xml file like as: <disk type='file' device='disk'> <driver name='qemu' type='qcow2'/> <source file='/var/lib/libvirt/images/guest.qcow2'/> <target dev='vda' bus='virtio'/> <transient/> </disk> When the qemu command line options are built, a new qcow2 image is created with backing qcow2 by using blockdev-snapshot command. The backing image is the qcow2 file which is set as <source>. The filename of the new qcow2 image is original-source-file.TRANSIENT. Signed-off-by: Masayoshi Mizuma <m.mizuma@xxxxxxxxxxxxxx> --- src/qemu/qemu_snapshot.c | 103 +++++++++++++++++++++++++++++++++++--- src/qemu/qemu_snapshot.h | 5 ++ src/util/virstoragefile.c | 2 + src/util/virstoragefile.h | 4 ++ 4 files changed, 107 insertions(+), 7 deletions(-) diff --git a/src/qemu/qemu_snapshot.c b/src/qemu/qemu_snapshot.c index 1e8ea80b22..67fdc488e0 100644 --- a/src/qemu/qemu_snapshot.c +++ b/src/qemu/qemu_snapshot.c @@ -1158,7 +1158,8 @@ qemuSnapshotCreateDiskActive(virQEMUDriverPtr driver, virHashTablePtr blockNamedNodeData, unsigned int flags, virQEMUDriverConfigPtr cfg, - qemuDomainAsyncJob asyncJob) + qemuDomainAsyncJob asyncJob, + bool domSave) { qemuDomainObjPrivatePtr priv = vm->privateData; g_autoptr(virJSONValue) actions = NULL; @@ -1201,17 +1202,26 @@ qemuSnapshotCreateDiskActive(virQEMUDriverPtr driver, virDomainAuditDisk(vm, dd->disk->src, dd->src, "snapshot", rc >= 0); - if (rc == 0) + if (rc == 0) { qemuSnapshotDiskUpdateSource(driver, vm, dd, blockdev); + + if (dd->disk->transient) { + /* Mark the transient working is completed to make sure we can */ + /* remove the transient disk when the guest is shutdown. */ + dd->disk->src->transientEstablished = true; + } + } } if (rc < 0) goto cleanup; - if (virDomainObjSave(vm, driver->xmlopt, cfg->stateDir) < 0 || - (vm->newDef && virDomainDefSave(vm->newDef, driver->xmlopt, - cfg->configDir) < 0)) - goto cleanup; + if (domSave) { + if (virDomainObjSave(vm, driver->xmlopt, cfg->stateDir) < 0 || + (vm->newDef && virDomainDefSave(vm->newDef, driver->xmlopt, + cfg->configDir) < 0)) + goto cleanup; + } ret = 0; @@ -1349,7 +1359,7 @@ qemuSnapshotCreateActiveExternal(virQEMUDriverPtr driver, if ((ret = qemuSnapshotCreateDiskActive(driver, vm, snap, blockNamedNodeData, flags, cfg, - QEMU_ASYNC_JOB_SNAPSHOT)) < 0) + QEMU_ASYNC_JOB_SNAPSHOT, true)) < 0) goto cleanup; /* the snapshot is complete now */ @@ -2264,3 +2274,82 @@ qemuSnapshotDelete(virDomainObjPtr vm, cleanup: return ret; } + + +static int +qemuSnapshotSetupTransientDisk(virDomainSnapshotDiskDefPtr def, + virStorageSourcePtr src) +{ + g_autoptr(virStorageSource) trans = NULL; + + if (!(trans = virStorageSourceNew())) + return -1; + + trans->path = g_strdup_printf("%s.TRANSIENT", src->path); + if (virFileExists(trans->path)) { + virReportError(VIR_ERR_INVALID_ARG, + _("Transient disk '%s' for '%s' exists"), + trans->path, src->path); + return -1; + } + + trans->type = VIR_STORAGE_TYPE_FILE; + trans->format = VIR_STORAGE_FILE_QCOW2; + + def->src = g_steal_pointer(&trans); + + return 0; +} + + +int +qemuSnapshotCreateTransientDisk(virQEMUDriverPtr driver, + virDomainObjPtr vm, + int asyncJob) +{ + int rc; + size_t i; + virDomainMomentObjPtr snap = NULL; + g_autoptr(virDomainSnapshotDef) snapdef = NULL; + g_autoptr(virHashTable) blockNamedNodeData = NULL; + qemuDomainObjPrivatePtr priv = vm->privateData; + g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(priv->driver); + + if (!(snapdef = virDomainSnapshotDefNew())) + return -1; + + snapdef->parent.name = g_strdup_printf("transient"); + + snapdef->ndisks = vm->def->ndisks; + if (VIR_ALLOC_N(snapdef->disks, snapdef->ndisks) < 0) + return -1; + + for (i = 0; i < vm->def->ndisks; i++) { + virDomainDiskDefPtr disk = vm->def->disks[i]; + virDomainSnapshotDiskDefPtr snapdisk = &(snapdef->disks[i]); + + if (disk->transient) { + if ((rc = qemuSnapshotSetupTransientDisk(snapdisk, disk->src)) < 0) + return -1; + + } else { + snapdisk->snapshot = VIR_DOMAIN_SNAPSHOT_LOCATION_NONE; + } + } + + if (!(snap = virDomainSnapshotAssignDef(vm->snapshots, snapdef))) + return -1; + + if (!(blockNamedNodeData = qemuBlockGetNamedNodeData(vm, asyncJob))) + goto cleanup; + + /* The last argument domSave is false here because transient disk config */ + /* is volatile so we don't need to save it. */ + rc = qemuSnapshotCreateDiskActive(driver, vm, snap, blockNamedNodeData, + 0, cfg, asyncJob, false); + + cleanup: + virDomainSnapshotObjListRemove(vm->snapshots, snap); + + return rc; +} diff --git a/src/qemu/qemu_snapshot.h b/src/qemu/qemu_snapshot.h index 8b3ebe87b1..d57ef4b8a4 100644 --- a/src/qemu/qemu_snapshot.h +++ b/src/qemu/qemu_snapshot.h @@ -53,3 +53,8 @@ int qemuSnapshotDelete(virDomainObjPtr vm, virDomainSnapshotPtr snapshot, unsigned int flags); + +int +qemuSnapshotCreateTransientDisk(virQEMUDriverPtr driver, + virDomainObjPtr vm, + int asyncJob); diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c index 97a346db28..636f27ef09 100644 --- a/src/util/virstoragefile.c +++ b/src/util/virstoragefile.c @@ -2456,6 +2456,8 @@ virStorageSourceCopy(const virStorageSource *src, def->ssh_host_key_check_disabled = src->ssh_host_key_check_disabled; def->ssh_user = g_strdup(src->ssh_user); + def->transientEstablished = src->transientEstablished; + return g_steal_pointer(&def); } diff --git a/src/util/virstoragefile.h b/src/util/virstoragefile.h index 87763cf389..0dc275c11e 100644 --- a/src/util/virstoragefile.h +++ b/src/util/virstoragefile.h @@ -384,6 +384,10 @@ struct _virStorageSource { /* these must not be used apart from formatting the output JSON in the qemu driver */ char *ssh_user; bool ssh_host_key_check_disabled; + + /* set to true if the storage source is setup as a transient disk. */ + /* The changes to the disk are dropped after the guest machine is shutdown. */ + bool transientEstablished; }; G_DEFINE_AUTOPTR_CLEANUP_FUNC(virStorageSource, virObjectUnref); -- 2.27.0