[libvirt PATCH 09/20] qemu_snapshot: introduce external snapshot revert support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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 <phrdina@xxxxxxxxxx>
---
 src/qemu/qemu_snapshot.c | 143 +++++++++++++++++++++++++++++++++++++--
 1 file changed, 139 insertions(+), 4 deletions(-)

diff --git a/src/qemu/qemu_snapshot.c b/src/qemu/qemu_snapshot.c
index 9e4b978b1b..af4e2ea6aa 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"
@@ -1968,6 +1970,119 @@ qemuSnapshotRevertWriteMetadata(virDomainObj *vm,
 }
 
 
+static int
+qemuSnapshotRevertExternal(virDomainObj *vm,
+                           virDomainMomentObj *snap,
+                           virDomainDef *config,
+                           virDomainDef *inactiveConfig,
+                           int *memsnapFD,
+                           char **memsnapPath)
+{
+    size_t i;
+    virDomainDef *domdef = NULL;
+    virDomainSnapshotLocation location = VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL;
+    virDomainMomentObj *curSnap = virDomainSnapshotGetCurrent(vm->snapshots);
+    virDomainSnapshotDef *snapdef = virDomainSnapshotObjGetDef(snap);
+    virDomainSnapshotDef *curdef = virDomainSnapshotObjGetDef(curSnap);
+    g_autoptr(virDomainSnapshotDef) tmpsnapdef = NULL;
+    g_autoptr(virBitmap) created = NULL;
+    int ret = -1;
+
+    if (config) {
+        domdef = config;
+    } else {
+        domdef = inactiveConfig;
+    }
+
+    if (!(tmpsnapdef = virDomainSnapshotDefNew()))
+        return -1;
+
+    if (virDomainMomentDefPostParse(&tmpsnapdef->parent) < 0)
+        return -1;
+
+    if (virDomainSnapshotAlignDisks(tmpsnapdef, domdef, location, false) < 0)
+        return -1;
+
+    created = virBitmapNew(tmpsnapdef->ndisks);
+
+    if (qemuSnapshotCreateQcow2Files(vm, domdef, tmpsnapdef, created, 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);
+    }
+
+    if (config) {
+        if (qemuSnapshotDomainDefUpdateDisk(config, tmpsnapdef, false) < 0)
+            goto cleanup;
+    }
+
+    if (qemuSnapshotDomainDefUpdateDisk(inactiveConfig, tmpsnapdef, false) < 0)
+        goto cleanup;
+
+    if (curdef->revertdisks) {
+        for (i = 0; i < curdef->nrevertdisks; i++) {
+            virDomainSnapshotDiskDef *snapdisk = &(curdef->revertdisks[i]);
+
+            if (unlink(snapdisk->src->path) < 0) {
+                VIR_WARN("Failed to remove snapshot image '%s'",
+                         snapdisk->src->path);
+            }
+
+            virDomainSnapshotDiskDefClear(snapdisk);
+        }
+
+        g_clear_pointer(&curdef->revertdisks, g_free);
+        curdef->nrevertdisks = 0;
+    } else {
+        for (i = 0; i < curdef->ndisks; i++) {
+            virDomainSnapshotDiskDef *snapdisk = &(curdef->disks[i]);
+
+            if (unlink(snapdisk->src->path) < 0) {
+                VIR_WARN("Failed to remove snapshot image '%s'",
+                         snapdisk->src->path);
+            }
+        }
+    }
+
+    if (snap->nchildren != 0) {
+        snapdef->revertdisks = g_steal_pointer(&tmpsnapdef->disks);
+        snapdef->nrevertdisks = tmpsnapdef->ndisks;
+        tmpsnapdef->ndisks = 0;
+    } else {
+        for (i = 0; i < snapdef->ndisks; i++) {
+            virDomainSnapshotDiskDefClear(&snapdef->disks[i]);
+        }
+        g_free(snapdef->disks);
+        snapdef->disks = g_steal_pointer(&tmpsnapdef->disks);
+        snapdef->ndisks = tmpsnapdef->ndisks;
+        tmpsnapdef->ndisks = 0;
+    }
+
+    ret = 0;
+
+ cleanup:
+    if (ret != 0 && created) {
+        ssize_t bit = -1;
+
+        while ((bit = virBitmapNextSetBit(created, bit)) >= 0) {
+            virDomainSnapshotDiskDef *snapdisk = &(tmpsnapdef->disks[bit]);
+
+            if (unlink(snapdisk->src->path) < 0) {
+                VIR_WARN("Failed to remove snapshot image '%s'",
+                         snapdisk->src->path);
+            }
+        }
+    }
+
+    return ret;
+}
+
+
 static int
 qemuSnapshotRevertActive(virDomainObj *vm,
                          virDomainSnapshotPtr snapshot,
@@ -1982,6 +2097,9 @@ qemuSnapshotRevertActive(virDomainObj *vm,
 {
     virObjectEvent *event = NULL;
     virObjectEvent *event2 = NULL;
+    virDomainMomentObj *loadSnap = NULL;
+    VIR_AUTOCLOSE memsnapFD = -1;
+    char *memsnapPath = NULL;
     int detail;
     bool defined = false;
     qemuDomainSaveCookie *cookie = (qemuDomainSaveCookie *) snapdef->cookie;
@@ -2003,6 +2121,15 @@ qemuSnapshotRevertActive(virDomainObj *vm,
         virObjectEventStateQueue(driver->domainEventState, event);
     }
 
+    if (virDomainSnapshotIsExternal(snap)) {
+        if (qemuSnapshotRevertExternal(vm, snap, *config, *inactiveConfig,
+                                       &memsnapFD, &memsnapPath) < 0) {
+            return -1;
+        }
+    } else {
+        loadSnap = snap;
+    }
+
     if (*inactiveConfig) {
         virDomainObjAssignDef(vm, inactiveConfig, false, NULL);
         defined = true;
@@ -2019,7 +2146,8 @@ qemuSnapshotRevertActive(virDomainObj *vm,
 
     rc = qemuProcessStart(snapshot->domain->conn, driver, vm,
                           cookie ? cookie->cpu : NULL,
-                          VIR_ASYNC_JOB_START, NULL, -1, NULL, snap,
+                          VIR_ASYNC_JOB_START, NULL, memsnapFD,
+                          memsnapPath, loadSnap,
                           VIR_NETDEV_VPORT_PROFILE_OP_CREATE,
                           start_flags);
     virDomainAuditStart(vm, "from-snapshot", rc >= 0);
@@ -2124,9 +2252,16 @@ qemuSnapshotRevertInactive(virDomainObj *vm,
         virObjectEventStateQueue(driver->domainEventState, event);
     }
 
-    if (qemuSnapshotInternalRevertInactive(driver, vm, snap) < 0) {
-        qemuDomainRemoveInactive(driver, vm, 0, false);
-        return -1;
+    if (virDomainSnapshotIsExternal(snap)) {
+        if (qemuSnapshotRevertExternal(vm, snap, NULL, *inactiveConfig,
+                                       NULL, NULL) < 0) {
+            return -1;
+        }
+    } else {
+        if (qemuSnapshotInternalRevertInactive(driver, vm, snap) < 0) {
+            qemuDomainRemoveInactive(driver, vm, 0, false);
+            return -1;
+        }
     }
 
     if (*inactiveConfig) {
-- 
2.39.2




[Index of Archives]     [Virt Tools]     [Libvirt Users]     [Lib OS Info]     [Fedora Users]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [KDE Users]     [Fedora Tools]

  Powered by Linux