On Tue, Jun 27, 2023 at 17:07:15 +0200, Pavel Hrdina wrote:
> When reverting to external snapshot we need to create new overlay qcow2
> files from the disk files the VM had when the snapshot was taken.
> 
> There are some specifics and limitations when reverting to a snapshot:
> 
> 1) When reverting to last snapshot we need to first create new overlay
>    files before we can safely delete the old overlay files in case the
>    creation fails so we have still recovery option when we error out.
> 
>    These new files will not have the suffix as when the snapshot was
>    created as renaming the original files in order to use the same file
>    names as when the snapshot was created would add unnecessary
>    complexity to the code.
> 
> 2) When reverting to any snapshot we will always create overlay files
>    for every disk the VM had when the snapshot was done. Otherwise we
>    would have to figure out if there is any other qcow2 image already
>    using any of the VM disks as backing store and that itself might be
>    extremely complex and in some cases impossible.
> 
> 3) When reverting from any state the current overlay files will be
>    always removed as that VM state is not meant to be saved. It's the
>    same as with internal snapshots. If user want's to keep the current
>    state before reverting they need to create a new snapshot. For now
>    this will only work if the current snapshot is the last.
> 
> Signed-off-by: Pavel Hrdina <phrd...@redhat.com>
> ---
>  src/qemu/qemu_snapshot.c | 232 ++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 228 insertions(+), 4 deletions(-)
> 
> diff --git a/src/qemu/qemu_snapshot.c b/src/qemu/qemu_snapshot.c
> index 1cb0ea55de..dbf2cdd5db 100644
> --- a/src/qemu/qemu_snapshot.c
> +++ b/src/qemu/qemu_snapshot.c
> @@ -18,6 +18,8 @@
>  
>  #include <config.h>
>  
> +#include <fcntl.h>
> +
>  #include "qemu_snapshot.h"
>  
>  #include "qemu_monitor.h"
> @@ -1975,6 +1977,183 @@ qemuSnapshotRevertWriteMetadata(virDomainObj *vm,
>  }

Please document all new functions both any non-obvious parameter and
what it is supposed to do.


> +static int
> +qemuSnapshotRevertExternalPrepare(virDomainObj *vm,
> +                                  virDomainSnapshotDef *tmpsnapdef,
> +                                  virDomainMomentObj *snap,
> +                                  virDomainDef *config,
> +                                  virDomainDef *inactiveConfig,
> +                                  int *memsnapFD,
> +                                  char **memsnapPath)
> +{
> +    ssize_t i;

Normally we declare 'i' as unsigned.

> +    bool active = virDomainObjIsActive(vm);
> +    virDomainDef *domdef = NULL;
> +    virDomainSnapshotLocation location = 
> VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL;

This constant is used in one place only.

> +    virDomainSnapshotDef *snapdef = virDomainSnapshotObjGetDef(snap);
> +
> +    if (config) {
> +        domdef = config;
> +    } else {
> +        domdef = inactiveConfig;
> +    }
> +
> +    /* We need this to generate creation timestamp that is used as default
> +     * snapshot name. */
> +    if (virDomainMomentDefPostParse(&tmpsnapdef->parent) < 0)
> +        return -1;
> +
> +    if (virDomainSnapshotAlignDisks(tmpsnapdef, domdef, location, false, 
> true) < 0)
> +        return -1;
> +
> +    for (i = 0; i < tmpsnapdef->ndisks; i++) {
> +        virDomainSnapshotDiskDef *snapdisk = &tmpsnapdef->disks[i];
> +        virDomainDiskDef *domdisk = domdef->disks[i];
> +
> +        if (qemuSnapshotPrepareDiskExternal(domdisk, snapdisk, active, 
> false) < 0)
> +            return -1;
> +    }
> +
> +    if (memsnapFD && memsnapPath && snapdef->memorysnapshotfile) {
> +        virQEMUDriver *driver = ((qemuDomainObjPrivate *) 
> vm->privateData)->driver;
> +        g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
> +
> +        *memsnapPath = snapdef->memorysnapshotfile;
> +        *memsnapFD = qemuDomainOpenFile(cfg, NULL, *memsnapPath, O_RDONLY, 
> NULL);

Since the caller inherits the FD this must be documented.

> +    }
> +
> +    return 0;
> +}
> +
> +
> +static int
> +qemuSnapshotRevertExternalActive(virDomainObj *vm,
> +                                 virDomainSnapshotDef *tmpsnapdef)
> +{
> +    ssize_t i;

'size_t'

> +    g_autoptr(GHashTable) blockNamedNodeData = NULL;
> +    g_autoptr(qemuSnapshotDiskContext) snapctxt = NULL;
> +
> +    snapctxt = qemuSnapshotDiskContextNew(tmpsnapdef->ndisks, vm, 
> VIR_ASYNC_JOB_SNAPSHOT);
> +
> +    if (!(blockNamedNodeData = qemuBlockGetNamedNodeData(vm, 
> VIR_ASYNC_JOB_SNAPSHOT)))
> +        return -1;
> +
> +    for (i = 0; i < tmpsnapdef->ndisks; i++) {
> +        if (qemuSnapshotDiskPrepareOne(snapctxt,
> +                                       vm->def->disks[i],
> +                                       tmpsnapdef->disks + i,
> +                                       blockNamedNodeData,
> +                                       false,
> +                                       true) < 0)
> +            return -1;
> +    }
> +
> +    if (qemuSnapshotDiskCreate(snapctxt) < 0)
> +        return -1;
> +
> +    return 0;
> +}
> +
> +
> +static int
> +qemuSnapshotRevertExternalInactive(virDomainObj *vm,
> +                                   virDomainSnapshotDef *tmpsnapdef,
> +                                   virDomainDef *config,
> +                                   virDomainDef *inactiveConfig)

This function is named '..Inactive'

> +{
> +    virDomainDef *domdef = NULL;
> +    g_autoptr(virBitmap) created = NULL;
> +
> +    created = virBitmapNew(tmpsnapdef->ndisks);
> +
> +    if (config) {
> +        domdef = config;
> +    } else {
> +        domdef = inactiveConfig;
> +    }

So this logic is weird. Inactive VMs have only one definition.

> +
> +    if (config) {
> +        if (qemuSnapshotDomainDefUpdateDisk(config, tmpsnapdef, false) < 0)
> +            return -1;
> +    }
> +
> +    if (qemuSnapshotDomainDefUpdateDisk(inactiveConfig, tmpsnapdef, false) < 
> 0)
> +        return -1;
> +
> +    if (qemuSnapshotCreateQcow2Files(vm, domdef, tmpsnapdef, created, false) 
> < 0) {
> +        ssize_t bit = -1;

Consider storing the error here ...

> +
> +        while ((bit = virBitmapNextSetBit(created, bit)) >= 0) {
> +            virDomainSnapshotDiskDef *snapdisk = &(tmpsnapdef->disks[bit]);
> +
> +            if (virStorageSourceInit(snapdisk->src) < 0 ||
> +                virStorageSourceUnlink(snapdisk->src) < 0) {

... so that this doesn't overwrite it.

> +                VIR_WARN("Failed to remove snapshot image '%s'",
> +                         snapdisk->src->path);
> +            }
> +        }
> +
> +        return -1;
> +    }
> +
> +    return 0;
> +}

The rest seems to make sense:

Reviewed-by: Peter Krempa <pkre...@redhat.com>

Reply via email to