[libvirt] [PATCH v8 4/4] vbox_tmpl.c: Add function for undefining snapshot

Yohan BELLEGUIC yohan.belleguic at diateam.net
Mon May 19 12:47:33 UTC 2014


All snapshots information will be deleted from the vbox XML, but
differencing disks will be kept so the user will be able to redefine the
snapshot.
---
 src/vbox/vbox_tmpl.c |  464 +++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 463 insertions(+), 1 deletion(-)

diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c
index a7f15d4..eb577f4 100644
--- a/src/vbox/vbox_tmpl.c
+++ b/src/vbox/vbox_tmpl.c
@@ -8364,7 +8364,454 @@ vboxDomainSnapshotDeleteTree(vboxGlobalData *data,
     return ret;
 }
 
+#if VBOX_API_VERSION >= 4002000
+static int
+vboxDomainSnapshotDeleteMetadataOnly(virDomainSnapshotPtr snapshot)
+{
+    /*
+     * This function will remove the node in the vbox xml corresponding to the snapshot.
+     * It is usually called by vboxDomainSnapshotDelete() with the flag
+     * VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY.
+     * If you want to use it anywhere else, be careful, if the snapshot you want to delete
+     * has children, the result is not granted, they will probably will be deleted in the
+     * xml, but you may have a problem with hard drives.
+     *
+     * If the snapshot which is being deleted is the current one, we will set the current
+     * snapshot of the machine to the parent of this snapshot. Before writing the modified
+     * xml file, we undefine the machine from vbox. After writing the file, we redefine
+     * the machine with the new file.
+     */
+
+    virDomainPtr dom = snapshot->domain;
+    VBOX_OBJECT_CHECK(dom->conn, int, -1);
+    virDomainSnapshotDefPtr def= NULL;
+    char *defXml = NULL;
+    vboxIID domiid = VBOX_IID_INITIALIZER;
+    nsresult rc;
+    IMachine *machine = NULL;
+    PRUnichar *settingsFilePathUtf16 = NULL;
+    char *settingsFilepath = NULL;
+    virVBoxSnapshotConfMachinePtr snapshotMachineDesc = NULL;
+    int isCurrent = -1;
+    int it = 0;
+    PRUnichar *machineNameUtf16 = NULL;
+    char *machineName = NULL;
+    char *nameTmpUse = NULL;
+    char *machineLocationPath = NULL;
+    PRUint32 aMediaSize = 0;
+    IMedium **aMedia = NULL;
 
+    defXml = vboxDomainSnapshotGetXMLDesc(snapshot, 0);
+    if (!defXml) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Unable to get XML Desc of snapshot"));
+        goto cleanup;
+    }
+    def = virDomainSnapshotDefParseString(defXml,
+                                          data->caps,
+                                          data->xmlopt,
+                                          -1,
+                                          VIR_DOMAIN_SNAPSHOT_PARSE_DISKS |
+                                          VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE);
+    if (!def) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Unable to get a virDomainSnapshotDefPtr"));
+        goto cleanup;
+    }
+
+    vboxIIDFromUUID(&domiid, dom->uuid);
+    rc = VBOX_OBJECT_GET_MACHINE(domiid.value, &machine);
+    if (NS_FAILED(rc)) {
+        virReportError(VIR_ERR_NO_DOMAIN, "%s",
+                       _("no domain with matching UUID"));
+        goto cleanup;
+    }
+    rc = machine->vtbl->GetSettingsFilePath(machine, &settingsFilePathUtf16);
+    if (NS_FAILED(rc)) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("cannot get settings file path"));
+        goto cleanup;
+    }
+    VBOX_UTF16_TO_UTF8(settingsFilePathUtf16, &settingsFilepath);
+
+    /*Getting the machine name to retrieve the machine location path.*/
+    rc = machine->vtbl->GetName(machine, &machineNameUtf16);
+    if (NS_FAILED(rc)) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("cannot get machine name"));
+        goto cleanup;
+    }
+    VBOX_UTF16_TO_UTF8(machineNameUtf16, &machineName);
+    if (virAsprintf(&nameTmpUse, "%s.vbox", machineName) < 0)
+        goto cleanup;
+    machineLocationPath = virStringReplace(settingsFilepath, nameTmpUse, "");
+    if (machineLocationPath == NULL) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Unable to get the machine location path"));
+        goto cleanup;
+    }
+    snapshotMachineDesc = virVBoxSnapshotConfLoadVboxFile(settingsFilepath, machineLocationPath);
+    if (!snapshotMachineDesc) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("cannot create a vboxSnapshotXmlPtr"));
+        goto cleanup;
+    }
+
+    isCurrent = virVBoxSnapshotConfIsCurrentSnapshot(snapshotMachineDesc, def->name);
+    if (isCurrent < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Unable to know if the snapshot is the current snapshot"));
+        goto cleanup;
+    }
+    if (isCurrent) {
+        /*
+         * If the snapshot is the current snapshot, it means that the machine has read-write
+         * disks. The first thing to do is to manipulate VirtualBox API to create
+         * differential read-write disks if the parent snapshot is not null.
+         */
+        if (def->parent != NULL) {
+            for (it = 0; it < def->dom->ndisks; it++) {
+                virVBoxSnapshotConfHardDiskPtr readOnly = NULL;
+                IMedium *medium = NULL;
+                PRUnichar *locationUtf16 = NULL;
+                PRUnichar *parentUuidUtf16 = NULL;
+                char *parentUuid = NULL;
+                IMedium *newMedium = NULL;
+                PRUnichar *formatUtf16 = NULL;
+                PRUnichar *newLocation = NULL;
+                char *newLocationUtf8 = NULL;
+                IProgress *progress = NULL;
+                PRInt32 resultCode = -1;
+                virVBoxSnapshotConfHardDiskPtr disk = NULL;
+                PRUnichar *uuidUtf16 = NULL;
+                char *uuid = NULL;
+                char *format = NULL;
+                char **searchResultTab = NULL;
+                ssize_t resultSize = 0;
+                char *tmp = NULL;
+
+                readOnly = virVBoxSnapshotConfHardDiskPtrByLocation(snapshotMachineDesc,
+                                                 def->dom->disks[it]->src.path);
+                if (!readOnly) {
+                    virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                                   _("Cannot get hard disk by location"));
+                    goto cleanup;
+                }
+                if (readOnly->parent == NULL) {
+                    virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                                   _("The read only disk has no parent"));
+                    goto cleanup;
+                }
+
+                VBOX_UTF8_TO_UTF16(readOnly->parent->location, &locationUtf16);
+                rc = data->vboxObj->vtbl->OpenMedium(data->vboxObj,
+                                                     locationUtf16,
+                                                     DeviceType_HardDisk,
+                                                     AccessMode_ReadWrite,
+                                                     false,
+                                                     &medium);
+                if (NS_FAILED(rc)) {
+                    virReportError(VIR_ERR_INTERNAL_ERROR,
+                                   _("Unable to open HardDisk, rc=%08x"),
+                                   (unsigned)rc);
+                    goto cleanup;
+                }
+
+                rc = medium->vtbl->GetId(medium, &parentUuidUtf16);
+                if (NS_FAILED(rc)) {
+                    virReportError(VIR_ERR_INTERNAL_ERROR,
+                                   _("Unable to get hardDisk Id, rc=%08x"),
+                                   (unsigned)rc);
+                    goto cleanup;
+                }
+                VBOX_UTF16_TO_UTF8(parentUuidUtf16, &parentUuid);
+                VBOX_UTF16_FREE(parentUuidUtf16);
+                VBOX_UTF16_FREE(locationUtf16);
+                VBOX_UTF8_TO_UTF16("VDI", &formatUtf16);
+
+                if (virAsprintf(&newLocationUtf8, "%sfakedisk-%s-%d.vdi",
+                                machineLocationPath, def->parent, it) < 0)
+                    goto cleanup;
+                VBOX_UTF8_TO_UTF16(newLocationUtf8, &newLocation);
+                rc = data->vboxObj->vtbl->CreateHardDisk(data->vboxObj,
+                                                         formatUtf16,
+                                                         newLocation,
+                                                         &newMedium);
+                if (NS_FAILED(rc)) {
+                    virReportError(VIR_ERR_INTERNAL_ERROR,
+                                   _("Unable to create HardDisk, rc=%08x"),
+                                   (unsigned)rc);
+                    goto cleanup;
+                }
+                VBOX_UTF16_FREE(formatUtf16);
+                VBOX_UTF16_FREE(newLocation);
+
+# if VBOX_API_VERSION < 4003000
+                medium->vtbl->CreateDiffStorage(medium, newMedium, MediumVariant_Diff, &progress);
+# else
+                PRUint32 tab[1];
+                tab[0] =  MediumVariant_Diff;
+                medium->vtbl->CreateDiffStorage(medium, newMedium, 1, tab, &progress);
+# endif
+
+                progress->vtbl->WaitForCompletion(progress, -1);
+                progress->vtbl->GetResultCode(progress, &resultCode);
+                if (NS_FAILED(resultCode)) {
+                    virReportError(VIR_ERR_INTERNAL_ERROR,
+                                   _("Error while creating diff storage, rc=%08x"),
+                                   (unsigned)resultCode);
+                    goto cleanup;
+                }
+                VBOX_RELEASE(progress);
+                /*
+                 * The differential disk is created, we add it to the media registry and
+                 * the machine storage controller.
+                 */
+
+                if (VIR_ALLOC(disk) < 0)
+                    goto cleanup;
+
+                rc = newMedium->vtbl->GetId(newMedium, &uuidUtf16);
+                if (NS_FAILED(rc)) {
+                    virReportError(VIR_ERR_INTERNAL_ERROR,
+                                   _("Unable to get medium uuid, rc=%08x"),
+                                   (unsigned)rc);
+                    goto cleanup;
+                }
+                VBOX_UTF16_TO_UTF8(uuidUtf16, &uuid);
+                disk->uuid = uuid;
+                VBOX_UTF16_FREE(uuidUtf16);
+
+                if (VIR_STRDUP(disk->location, newLocationUtf8) < 0)
+                    goto cleanup;
+
+                rc = newMedium->vtbl->GetFormat(newMedium, &formatUtf16);
+                VBOX_UTF16_TO_UTF8(formatUtf16, &format);
+                disk->format = format;
+                VBOX_UTF16_FREE(formatUtf16);
+
+                if (virVBoxSnapshotConfAddHardDiskToMediaRegistry(disk,
+                                               snapshotMachineDesc->mediaRegistry,
+                                               parentUuid) < 0) {
+                    virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                                   _("Unable to add hard disk to the media registry"));
+                    goto cleanup;
+                }
+                /*Adding fake disks to the machine storage controllers*/
+
+                resultSize = virStringSearch(snapshotMachineDesc->storageController,
+                                             VBOX_UUID_REGEX,
+                                             it + 1,
+                                             &searchResultTab);
+                if (resultSize != it + 1) {
+                    virReportError(VIR_ERR_INTERNAL_ERROR,
+                                   _("Unable to find UUID %s"), searchResultTab[it]);
+                    goto cleanup;
+                }
+
+                tmp = virStringReplace(snapshotMachineDesc->storageController,
+                                       searchResultTab[it],
+                                       disk->uuid);
+                virStringFreeList(searchResultTab);
+                VIR_FREE(snapshotMachineDesc->storageController);
+                if (!tmp)
+                    goto cleanup;
+                if (VIR_STRDUP(snapshotMachineDesc->storageController, tmp) < 0)
+                    goto cleanup;
+
+                VIR_FREE(tmp);
+                /*Closing the "fake" disk*/
+                rc = newMedium->vtbl->Close(newMedium);
+                if (NS_FAILED(rc)) {
+                    virReportError(VIR_ERR_INTERNAL_ERROR,
+                                   _("Unable to close the new medium, rc=%08x"),
+                                   (unsigned)rc);
+                    goto cleanup;
+                }
+            }
+        } else {
+            for (it = 0; it < def->dom->ndisks; it++) {
+                char *uuidRO = NULL;
+                char **searchResultTab = NULL;
+                ssize_t resultSize = 0;
+                char *tmp = NULL;
+                uuidRO = virVBoxSnapshotConfHardDiskUuidByLocation(snapshotMachineDesc,
+                                                      def->dom->disks[it]->src.path);
+                if (!uuidRO) {
+                    virReportError(VIR_ERR_INTERNAL_ERROR,
+                                   _("No such disk in media registry %s"),
+                                   def->dom->disks[it]->src.path);
+                    goto cleanup;
+                }
+
+                resultSize = virStringSearch(snapshotMachineDesc->storageController,
+                                             VBOX_UUID_REGEX,
+                                             it + 1,
+                                             &searchResultTab);
+                if (resultSize != it + 1) {
+                    virReportError(VIR_ERR_INTERNAL_ERROR,
+                                   _("Unable to find UUID %s"),
+                                   searchResultTab[it]);
+                    goto cleanup;
+                }
+
+                tmp = virStringReplace(snapshotMachineDesc->storageController,
+                                       searchResultTab[it],
+                                       uuidRO);
+                virStringFreeList(searchResultTab);
+                VIR_FREE(snapshotMachineDesc->storageController);
+                if (!tmp)
+                    goto cleanup;
+                if (VIR_STRDUP(snapshotMachineDesc->storageController, tmp) < 0)
+                    goto cleanup;
+
+                VIR_FREE(tmp);
+            }
+        }
+    }
+    /*We remove the read write disks from the media registry*/
+    for (it = 0; it < def->ndisks; it++) {
+        char *uuidRW = virVBoxSnapshotConfHardDiskUuidByLocation(snapshotMachineDesc, def->disks[it].src.path);
+        if (!uuidRW) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("Unable to find UUID for location %s"), def->disks[it].src.path);
+            goto cleanup;
+        }
+        if (virVBoxSnapshotConfRemoveHardDisk(snapshotMachineDesc->mediaRegistry, uuidRW) < 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("Unable to remove disk from media registry. uuid = %s"), uuidRW);
+            goto cleanup;
+        }
+    }
+    /*If the parent snapshot is not NULL, we remove the-read only disks from the media registry*/
+    if (def->parent != NULL) {
+        for (it = 0; it < def->dom->ndisks; it++) {
+            char *uuidRO = virVBoxSnapshotConfHardDiskUuidByLocation(snapshotMachineDesc, def->dom->disks[it]->src.path);
+            if (!uuidRO) {
+                virReportError(VIR_ERR_INTERNAL_ERROR,
+                               _("Unable to find UUID for location %s"), def->dom->disks[it]->src.path);
+                goto cleanup;
+            }
+            if (virVBoxSnapshotConfRemoveHardDisk(snapshotMachineDesc->mediaRegistry, uuidRO) < 0) {
+                virReportError(VIR_ERR_INTERNAL_ERROR,
+                               _("Unable to remove disk from media registry. uuid = %s"), uuidRO);
+                goto cleanup;
+            }
+        }
+    }
+    rc = machine->vtbl->Unregister(machine,
+                              CleanupMode_DetachAllReturnHardDisksOnly,
+                              &aMediaSize,
+                              &aMedia);
+    if (NS_FAILED(rc)) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Unable to unregister machine, rc=%08x"),
+                       (unsigned)rc);
+        goto cleanup;
+    }
+    VBOX_RELEASE(machine);
+    for (it = 0; it < aMediaSize; it++) {
+        IMedium *medium = aMedia[it];
+        if (medium) {
+            PRUnichar *locationUtf16 = NULL;
+            char *locationUtf8 = NULL;
+            rc = medium->vtbl->GetLocation(medium, &locationUtf16);
+            VBOX_UTF16_TO_UTF8(locationUtf16, &locationUtf8);
+            if (isCurrent && strstr(locationUtf8, "fake") != NULL) {
+                /*we delete the fake disk because we don't need it anymore*/
+                IProgress *progress = NULL;
+                PRInt32 resultCode = -1;
+                rc = medium->vtbl->DeleteStorage(medium, &progress);
+                if (NS_FAILED(rc)) {
+                    virReportError(VIR_ERR_INTERNAL_ERROR,
+                                   _("Unable to delete medium, rc=%08x"),
+                                   (unsigned)rc);
+                    goto cleanup;
+                }
+                progress->vtbl->WaitForCompletion(progress, -1);
+                progress->vtbl->GetResultCode(progress, &resultCode);
+                if (NS_FAILED(resultCode)) {
+                    virReportError(VIR_ERR_INTERNAL_ERROR,
+                                   _("Error while closing medium, rc=%08x"),
+                                   (unsigned)resultCode);
+                    goto cleanup;
+                }
+                VBOX_RELEASE(progress);
+            } else {
+                /* This a comment from vboxmanage code in the handleUnregisterVM
+                 * function in VBoxManageMisc.cpp :
+                 * Note that the IMachine::Unregister method will return the medium
+                 * reference in a sane order, which means that closing will normally
+                 * succeed, unless there is still another machine which uses the
+                 * medium. No harm done if we ignore the error. */
+                rc = medium->vtbl->Close(medium);
+            }
+            VBOX_UTF16_FREE(locationUtf16);
+            VBOX_UTF8_FREE(locationUtf8);
+        }
+    }
+
+    /*removing the snapshot*/
+    if (virVBoxSnapshotConfRemoveSnapshot(snapshotMachineDesc, def->name) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Unable to remove snapshot %s"), def->name);
+        goto cleanup;
+    }
+
+    if (isCurrent) {
+        VIR_FREE(snapshotMachineDesc->currentSnapshot);
+        if (def->parent != NULL) {
+            virVBoxSnapshotConfSnapshotPtr snap = virVBoxSnapshotConfSnapshotByName(snapshotMachineDesc->snapshot, def->parent);
+            if (!snap) {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                               _("Unable to get the snapshot to remove"));
+                goto cleanup;
+            }
+            if (VIR_STRDUP(snapshotMachineDesc->currentSnapshot, snap->uuid) < 0)
+                goto cleanup;
+        }
+    }
+
+    /*Registering the machine*/
+    if (virVBoxSnapshotConfSaveVboxFile(snapshotMachineDesc, settingsFilepath) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Unable to serialize the machine description"));
+        goto cleanup;
+    }
+    rc = data->vboxObj->vtbl->OpenMachine(data->vboxObj,
+                                     settingsFilePathUtf16,
+                                     &machine);
+    if (NS_FAILED(rc)) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Unable to open Machine, rc=%08x"),
+                       (unsigned)rc);
+        goto cleanup;
+    }
+
+    rc = data->vboxObj->vtbl->RegisterMachine(data->vboxObj, machine);
+    if (NS_FAILED(rc)) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Unable to register Machine, rc=%08x"),
+                       (unsigned)rc);
+        goto cleanup;
+    }
+
+    ret = 0;
+ cleanup:
+    VIR_FREE(def);
+    VIR_FREE(defXml);
+    VBOX_RELEASE(machine);
+    VBOX_UTF16_FREE(settingsFilePathUtf16);
+    VBOX_UTF8_FREE(settingsFilepath);
+    VIR_FREE(snapshotMachineDesc);
+    VBOX_UTF16_FREE(machineNameUtf16);
+    VBOX_UTF8_FREE(machineName);
+    VIR_FREE(machineLocationPath);
+    VIR_FREE(nameTmpUse);
+
+    return ret;
+}
+#endif
 
 static int
 vboxDomainSnapshotDelete(virDomainSnapshotPtr snapshot,
@@ -8378,6 +8825,7 @@ vboxDomainSnapshotDelete(virDomainSnapshotPtr snapshot,
     IConsole *console = NULL;
     PRUint32 state;
     nsresult rc;
+    vboxArray snapChildren = VBOX_ARRAY_INITIALIZER;
 
     virCheckFlags(VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN |
                   VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY, -1);
@@ -8405,7 +8853,21 @@ vboxDomainSnapshotDelete(virDomainSnapshotPtr snapshot,
      *to remove the node concerning the snapshot
     */
     if (flags & VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY) {
-        ret = 0;
+        rc = vboxArrayGet(&snapChildren, snap, snap->vtbl->GetChildren);
+        if (NS_FAILED(rc)) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("could not get snapshot children"));
+            goto cleanup;
+        }
+        if (snapChildren.count != 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("cannot delete metadata of a snapshot with children"));
+            goto cleanup;
+        } else {
+#if VBOX_API_VERSION >= 4002000
+            ret = vboxDomainSnapshotDeleteMetadataOnly(snapshot);
+#endif
+        }
         goto cleanup;
     }
 
-- 
1.7.10.4




More information about the libvir-list mailing list