[libvirt] [PATCHv3 RESEND 5/5] vbox_tmpl.c: Add methods for undefining snapshots

Manuel VIVES manuel.vives at diateam.net
Mon Sep 16 14:28:18 UTC 2013


All the informations concerning snapshots (and snapshot disks)
will be deleted from the vbox xml. But the differencing disks will be
kept so you will be able to redefine the snapshots.
---
 src/vbox/vbox_tmpl.c |  387 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 387 insertions(+)

diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c
index 2b07dec..4bc1054 100644
--- a/src/vbox/vbox_tmpl.c
+++ b/src/vbox/vbox_tmpl.c
@@ -6082,6 +6082,68 @@ vboxSnapshotXmlAppendDiskToMediaRegistry(xmlNodePtr *inMediaRegistry,
     }
 }
 
+
+static void
+vboxRemoveAllDisksExceptParentFromMediaRegistry(xmlNodePtr mediaRegistryNode){
+    xmlNodePtr cur_node = NULL;
+    for (cur_node = mediaRegistryNode; cur_node; cur_node = cur_node->next) {
+        if (cur_node) {
+            if (cur_node->type == XML_ELEMENT_NODE
+                && !xmlStrcmp(cur_node->name, (const xmlChar *) "HardDisk")) {
+                xmlNodePtr child = NULL;
+                for (child = cur_node->children; child; child = child->next) {
+                    /*We look over all the children
+                     *If there is a node element, we delete it
+                     */
+                    if (child->type == XML_ELEMENT_NODE) {
+                        xmlUnlinkNode(child);
+                        xmlFreeNode(child);
+                    }
+                }
+            }
+        }
+        if ((cur_node->children))
+            vboxRemoveAllDisksExceptParentFromMediaRegistry((cur_node->children));
+    }
+}
+
+static void
+vboxRemoveDiskFromMediaRegistryIfNoChildren(xmlNodePtr mediaRegistryNode,
+                                            char *diskLocation)
+{
+    /*
+     *This function will remove a disk from the media registry only if it doesn't
+     *have any children
+     */
+    xmlNodePtr cur_node = NULL;
+    for (cur_node = mediaRegistryNode; cur_node; cur_node = cur_node->next) {
+        if (cur_node) {
+            if (cur_node->type == XML_ELEMENT_NODE
+                && !xmlStrcmp(cur_node->name, (const xmlChar *) "HardDisk")
+                && xmlHasProp(cur_node, BAD_CAST "location") != NULL
+                && strstr(diskLocation, (char *)xmlHasProp(cur_node, BAD_CAST "location")->children->content) != NULL) {
+
+                xmlNodePtr child = NULL;
+                bool deleteNode = true;
+                for (child = cur_node->children; child; child = child->next) {
+                    /*We look over all the children
+                     *If there is a node element, we don't delete it
+                     */
+                    if (child->type == XML_ELEMENT_NODE)
+                        deleteNode = false;
+                }
+                if (deleteNode) {
+                    xmlUnlinkNode(cur_node);
+                    xmlFreeNode(cur_node);
+                }
+                return;
+            }
+        }
+        if ((cur_node->children))
+            vboxRemoveDiskFromMediaRegistryIfNoChildren((cur_node->children), diskLocation);
+    }
+}
+
 static int
 vboxSnapshotGenerateVboxXML(xmlNodePtr rootElementVboxXML,
                             char *storageControllerString,
@@ -8022,7 +8084,314 @@ cleanup:
     vboxArrayRelease(&children);
     return ret;
 }
+#if VBOX_API_VERSION >= 4002
+static int
+vboxCloseDisk(virDomainPtr dom,
+                  IMedium *baseDisk) {
+    VBOX_OBJECT_CHECK(dom->conn, int, -1);
+    nsresult rc;
+    vboxArray childrenDiskArray = VBOX_ARRAY_INITIALIZER;
+    size_t i = 0;
+    if (!baseDisk)
+        return -1;
+
+    rc = vboxArrayGet(&childrenDiskArray, baseDisk, baseDisk->vtbl->GetChildren);
+    if (NS_FAILED(rc)) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("could not get children disks"));
+        ret = -1;
+        goto cleanup;
+    }
+    for (i=0; i < childrenDiskArray.count; ++i)
+        vboxCloseDisk(dom, childrenDiskArray.items[i]);
+
+    baseDisk->vtbl->Close(baseDisk);
+    ret = 0;
+cleanup:
+    vboxArrayRelease(&childrenDiskArray);
+    return ret;
+}
+
+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, we will set the current snapshot of the machine to
+     *its parent.
+     *
+     *Before the writing of the modified xml file, we undefine the machine from vbox
+     *After the modification, we redefine the machine
+     */
+
+    virDomainPtr dom = snapshot->domain;
+    VBOX_OBJECT_CHECK(dom->conn, int, -1);
+    IMachine *machine = NULL;
+    IMachine *newMachine = NULL;
+    nsresult rc;
+    ISnapshot *snap = NULL;
+    vboxIID domiid = VBOX_IID_INITIALIZER;
+    vboxArray machineDisks = VBOX_ARRAY_INITIALIZER;
+    IMedium *dummyArray[] = { NULL };
+    PRUnichar *settingsFilePath = NULL;
+    char *settingsFilePathUtf8 = NULL;
+    xmlDocPtr xml = NULL;
+    xmlNodePtr xmlRootElement = NULL;
+    xmlNodePtr snapshotToDelete = NULL;
+    IProgress *progress = NULL;
+    vboxIID snapIid = VBOX_IID_INITIALIZER;
+    char *snapshotUuidStr = NULL;
+    char *snapshotUuidStrWithBrackets = NULL;
+    ISnapshot *parentSnapshot = NULL;
+    vboxIID parentIId = VBOX_IID_INITIALIZER;
+    char *parentSnapshotUuidStr = NULL;
+    char *parentSnapshotUuidStrWithBrackets = NULL;
+    virDomainSnapshotDefPtr def = NULL;
+    char *snapXmlDesc = NULL;
+    xmlNodePtr mediaRegistryNode = NULL;
+    xmlNodePtr storageControllersNode = NULL;
+    xmlBufferPtr buf = xmlBufferCreate();
+    xmlNodePtr newStorageControllersNode = NULL;
+    xmlNodePtr machineNode = NULL;
+    bool snapshotHasParent = false;
+    char *snapshotStorageControllersString = NULL;
+    size_t i = 0;
+    unsigned int numberOfDisks = 0;
+    unsigned int failedCounter = 10;
+
+
+    VIR_DEBUG("vboxDomainSnapshotDeleteMetadataOnly()");
+    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;
+    }
+
+    /*Get snapshot*/
+    snap = vboxDomainSnapshotGet(data, dom, machine, snapshot->name);
+    if (!snap)
+        goto cleanup;
+    rc = snap->vtbl->GetId(snap, &snapIid.value);
+    if (NS_FAILED(rc)) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("cannot get snapshot uuid"));
+        ret = -1;
+        goto cleanup;
+    }
+    snapXmlDesc = vboxDomainSnapshotGetXMLDesc(snapshot, 0);
+    if (!(def = virDomainSnapshotDefParseString(snapXmlDesc, data->caps,
+                                                data->xmlopt, -1, VIR_DOMAIN_SNAPSHOT_PARSE_DISKS | VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE)))
+        goto cleanup;
+    snapshotHasParent = !(def->parent == NULL);
+    /*Get its parent because it's possible that we have to put the parent uuid in
+     *the current snapshot attribute*/
+    rc = snap->vtbl->GetParent(snap, &parentSnapshot);
+    reportInternalErrorIfNS_FAILED("cannot get parent snapshot");
+    if (parentSnapshot) {
+        rc = parentSnapshot->vtbl->GetId(parentSnapshot, &parentIId.value);
+        reportInternalErrorIfNS_FAILED("cannot get parent snapshot id");
+        VBOX_UTF16_TO_UTF8(parentIId.value, &parentSnapshotUuidStr);
+        if (virAsprintf(&parentSnapshotUuidStrWithBrackets, "{%s}", parentSnapshotUuidStr) < 0) {
+            VIR_DEBUG("problem while using virasprintf");
+            goto cleanup;
+        }
+    }
+
+    rc = machine->vtbl->GetSettingsFilePath(machine, &settingsFilePath);
+    reportInternalErrorIfNS_FAILED("cannot get settings file path");
+
+    /*We ask libxml2 to parse the xmlfile*/
+    VBOX_UTF16_TO_UTF8(settingsFilePath, &settingsFilePathUtf8);
+    xml = virXMLParse(settingsFilePathUtf8, NULL, NULL);
+    xmlKeepBlanksDefault(1);
+    if (!xml) {
+        VIR_DEBUG("Cannot parse settings file");
+        ret = -1;
+        goto cleanup;
+    }
+
+    xmlRootElement = xmlDocGetRootElement(xml);
+    if (!xmlRootElement) {
+        VIR_DEBUG("Cannot find any root element in the xml");
+        ret = -1;
+        goto cleanup;
+    }
+
+    vboxSnapshotXmlRetrieveSnapshotNodeByName(xmlRootElement->children, snapshot->name, &snapshotToDelete);
+    if (!snapshotToDelete) {
+        VIR_DEBUG("Cannot find any snapshot to delete");
+        ret = -1;
+        goto cleanup;
+    }
+    /*We manage the case where the snapshot we want to delete is the current one */
+    VBOX_UTF16_TO_UTF8(snapIid.value, &snapshotUuidStr);
+    if (virAsprintf(&snapshotUuidStrWithBrackets, "{%s}", snapshotUuidStr) < 0) {
+        VIR_DEBUG("problem while using virasprintf");
+        ret = -1;
+        goto cleanup;
+    }
+    vboxSnapshotXmlRetrieveMachineNode(xmlRootElement, &machineNode);
+    /*we change the current snapshot if necessary*/
+    if (STREQ(virXMLPropString(machineNode, "currentSnapshot"), snapshotUuidStrWithBrackets)) {
+        if (parentSnapshotUuidStrWithBrackets != NULL) {
+            xmlSetProp(machineNode, BAD_CAST "currentSnapshot", BAD_CAST parentSnapshotUuidStrWithBrackets);
+        } else {
+            xmlUnsetProp(machineNode, BAD_CAST "currentSnapshot");
+        }
+    }
+    vboxSnapshotXmlRetrieveRootNodeByName(xmlRootElement, "MediaRegistry", &mediaRegistryNode);
+    if (!mediaRegistryNode) {
+        VIR_DEBUG("Problem while retrieving MediaRegistry node");
+        ret = -1;
+        goto cleanup;
+    }
+    vboxSnapshotXmlRetrieveRootNodeByName(xmlRootElement, "StorageControllers", &storageControllersNode);
+    if (!storageControllersNode) {
+        VIR_DEBUG("Problem while retrieving StorageControllers node");
+        ret = -1;
+        goto cleanup;
+    }
+    xmlNodeDump(buf, xml, storageControllersNode, 0, 0);
+    if (VIR_STRDUP(snapshotStorageControllersString, (char *)xmlBufferContent(buf)) < 0) {
+        VIR_DEBUG("Could not duplicate %s", (char *)xmlBufferContent(buf));
+    }
+    xmlBufferFree(buf);
+    if (snapshotHasParent)
+        numberOfDisks = def->dom->ndisks;
+    else
+        numberOfDisks = def->ndisks;
+
+    for (i = 0; i < numberOfDisks; ++i) {
+        IMedium *diskSnapshot = NULL;
+        IMedium *baseDiskSnapshot = NULL;
+        PRUnichar *locationDiskSnapshotUtf16 = NULL;
+        PRUnichar *baseDiskIdUtf16 = NULL;
+        char *baseDiskId = NULL;
+        char *diskPath = NULL;
+        char *uuidToReplace = NULL;
+        if (snapshotHasParent)
+            diskPath = def->dom->disks[i]->src;
+        else
+            diskPath = def->disks[i].file;
+        VBOX_UTF8_TO_UTF16(diskPath, &locationDiskSnapshotUtf16);
+
+        rc = data->vboxObj->vtbl->OpenMedium(data->vboxObj, locationDiskSnapshotUtf16, DeviceType_HardDisk, AccessMode_ReadWrite, false, &diskSnapshot);
+        reportInternalErrorIfNS_FAILED("cannot open medium");
+        if (!diskSnapshot) {
+            VIR_DEBUG("Could not open %s", def->dom->disks[i]->src);
+            ret = -1;
+            goto cleanup;
+        }
+        rc = diskSnapshot->vtbl->GetBase(diskSnapshot, &baseDiskSnapshot);
+        reportInternalErrorIfNS_FAILED("cannot get base disk");
+        if (!baseDiskSnapshot) {
+            VIR_DEBUG("Could not get base medium");
+            ret = -1;
+            goto cleanup;
+        }
+        rc = baseDiskSnapshot->vtbl->GetId(baseDiskSnapshot, &baseDiskIdUtf16);
+        reportInternalErrorIfNS_FAILED("cannot get base disk id");
+        VBOX_UTF16_TO_UTF8(baseDiskIdUtf16, &baseDiskId);
+        VIR_DEBUG("Removing disk %s from media registry", diskPath);
+        if (snapshotHasParent) {
+            vboxRemoveDiskFromMediaRegistryIfNoChildren(mediaRegistryNode->children, diskPath);
+        } else {
+            vboxRemoveAllDisksExceptParentFromMediaRegistry(mediaRegistryNode->children);
+            virSearchUuid(snapshotStorageControllersString, i+1,&uuidToReplace);
+            char *tmp = NULL;
+            tmp = virStrReplace(snapshotStorageControllersString, uuidToReplace, baseDiskId);
+            VIR_FREE(snapshotStorageControllersString);
+            if (VIR_STRDUP(snapshotStorageControllersString, tmp) < 0) {
+                VIR_DEBUG("Error while using strdup");
+            }
+            VIR_FREE(tmp);
+            xmlParseInNodeContext(machineNode, snapshotStorageControllersString, (int)strlen(snapshotStorageControllersString), 0, &newStorageControllersNode);
+        }
+        VBOX_UTF16_FREE(locationDiskSnapshotUtf16);
+        VBOX_UTF16_FREE(baseDiskIdUtf16);
+    }
+    /*we remove the machine from vbox*/
+    rc = vboxArrayGetWithUintArg(&machineDisks, machine, machine->vtbl->Unregister, CleanupMode_DetachAllReturnNone);
+    reportInternalErrorIfNS_FAILED("cannot unregister machine");
+    rc = machine->vtbl->Delete(machine, 0, dummyArray, &progress);
+    reportInternalErrorIfNS_FAILED("cannot delete machine");
+    if (progress != NULL) {
+        progress->vtbl->WaitForCompletion(progress, -1);
+        VBOX_RELEASE(progress);
+    }
+
+    for (i = 0; i < numberOfDisks; ++i) {
+        IMedium *diskSnapshot= NULL;
+        PRUnichar *locationDiskSnapshotUtf16 = NULL;
+        IMedium *baseDiskSnapshot = NULL;
+
+        VBOX_UTF8_TO_UTF16(def->dom->disks[i]->src, &locationDiskSnapshotUtf16);
+        rc = data->vboxObj->vtbl->OpenMedium(data->vboxObj, locationDiskSnapshotUtf16, DeviceType_HardDisk, AccessMode_ReadWrite, false, &diskSnapshot);
+        reportInternalErrorIfNS_FAILED("cannot open medium");
+        rc = diskSnapshot->vtbl->GetBase(diskSnapshot, &baseDiskSnapshot);
+        reportInternalErrorIfNS_FAILED("cannot get disk base");
+        vboxCloseDisk(dom, baseDiskSnapshot);
+    }
+
+    /*we delete the xml part corresponding to the snapshot*/
+    xmlUnlinkNode(snapshotToDelete);
+    xmlFreeNode(snapshotToDelete);
+
+    /*We delete the part corresponding to the storagecontrollers if we need to*/
+    if (numberOfDisks > 0 && !snapshotHasParent) {
+        xmlUnlinkNode(storageControllersNode);
+        xmlFreeNode(storageControllersNode);
+        xmlAddChild(machineNode, newStorageControllersNode);
+    }
+
+    /*we save the modified xml*/
+    if (virFileMakePath(mdir_name(settingsFilePathUtf8)) < 0)
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s"
+                       " %s",
+                       _("Cannot create path :"),
+                       settingsFilePathUtf8);
+    if (xmlSaveFormatFileEnc(settingsFilePathUtf8, xml, "ISO-8859-1", 1) <= 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s"
+                       " %s",
+                       _("Cannot save xml file to:"),
+                       settingsFilePathUtf8);
+    }
+    rc = data->vboxObj->vtbl->OpenMachine(data->vboxObj, settingsFilePath, &newMachine);
+    while (NS_FAILED(rc) && (rc == 0x80004005 /*NSCOM standard error*/) && failedCounter != 0) {
+        /*There is some random fails happening and they are not the error
+         *supposed to happen.
+         *I didn't have some solution on #vbox-dev
+         *but it appears that with another try it works so that's why we are trying 10 times maximum
+         *
+        */
+        rc = data->vboxObj->vtbl->OpenMachine(data->vboxObj, settingsFilePath, &newMachine);
+        failedCounter--;
+    }
+    reportInternalErrorIfNS_FAILED("cannot open machine");
+    if (newMachine) {
+        rc = data->vboxObj->vtbl->RegisterMachine(data->vboxObj, newMachine);
+        reportInternalErrorIfNS_FAILED("cannot register machine");
+    }
+    else {
+        ret = -1;
+        goto cleanup;
+    }
+
+    ret = 0;
+cleanup:
+    vboxArrayRelease(&machineDisks);
+    VBOX_UTF16_FREE(settingsFilePath);
+    return ret;
 
+}
+#endif
 static int
 vboxDomainSnapshotDelete(virDomainSnapshotPtr snapshot,
                          unsigned int flags)
@@ -8035,6 +8404,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);
@@ -8062,6 +8432,23 @@ vboxDomainSnapshotDelete(virDomainSnapshotPtr snapshot,
      *to remove the node concerning the snapshot
     */
     if (flags & VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY) {
+        rc = vboxArrayGet(&snapChildren, snap, snap->vtbl->GetChildren);
+        if (NS_FAILED(rc)) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("could not get snapshot children"));
+            ret = -1;
+            goto cleanup;
+        }
+        if (snapChildren.count != 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("cannot delete metadata of a snapshot with children"));
+            ret = -1;
+            goto cleanup;
+        } else {
+#if VBOX_API_VERSION >= 4002
+            vboxDomainSnapshotDeleteMetadataOnly(snapshot);
+#endif
+        }
         ret = 0;
         goto cleanup;
     }
-- 
1.7.10.4




More information about the libvir-list mailing list