[libvirt] [PATCHv2 RESEND 4/5] vbox_tmpl.c: Patch for redefining snapshots

Manuel VIVES manuel.vives at diateam.net
Mon Sep 2 16:05:52 UTC 2013


The snapshots are saved in xml files, and then can be redefined
---
 src/vbox/vbox_tmpl.c |  848 +++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 840 insertions(+), 8 deletions(-)

diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c
index ded179f..2b07dec 100644
--- a/src/vbox/vbox_tmpl.c
+++ b/src/vbox/vbox_tmpl.c
@@ -61,6 +61,7 @@
 #include "virstring.h"
 #include "virtime.h"
 #include "virutil.h"
+#include "dirname.h"
 
 /* This one changes from version to version. */
 #if VBOX_API_VERSION == 2002
@@ -274,6 +275,12 @@ static vboxGlobalData *g_pVBoxGlobalData = NULL;
 
 #endif /* VBOX_API_VERSION >= 4000 */
 
+/*This error is a bit specific
+ *In the VBOX API it is named E_ACCESSDENIED
+ *It is returned when the called object is not ready. In
+ *particular when we do any call on a disk which has been closed
+*/
+#define VBOX_E_ACCESSDENIED 0x80070005
 #define reportInternalErrorIfNS_FAILED(message) \
     if (NS_FAILED(rc)) { \
         virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _(message)); \
@@ -284,6 +291,8 @@ static vboxGlobalData *g_pVBoxGlobalData = NULL;
 static virDomainPtr vboxDomainDefineXML(virConnectPtr conn, const char *xml);
 static int vboxDomainCreate(virDomainPtr dom);
 static int vboxDomainUndefineFlags(virDomainPtr dom, unsigned int flags);
+static virStorageVolPtr vboxStorageVolLookupByPath(virConnectPtr conn, const char *path);
+static int vboxStorageDeleteOrClose(virStorageVolPtr vol, unsigned int flags, unsigned int flagDeleteOrClose);
 static void vboxDriverLock(vboxGlobalData *data) {
     virMutexLock(&data->lock);
 }
@@ -5896,6 +5905,823 @@ cleanup:
     return snapshot;
 }
 
+#if VBOX_API_VERSION >=4002
+static void
+vboxSnapshotXmlRetrieveSnapshotNodeByName(xmlNodePtr a_node,
+                               const char *name,
+                               xmlNodePtr *snap_node)
+{
+    xmlNodePtr cur_node = NULL;
+
+    for (cur_node = a_node; cur_node; cur_node = cur_node->next) {
+        if (cur_node->type == XML_ELEMENT_NODE) {
+            if (!xmlStrcmp(cur_node->name, (const xmlChar *) "Snapshot") &&
+                STREQ(virXMLPropString(cur_node, "name"), name)) {
+                *snap_node = cur_node;
+                return;
+            }
+        }
+        if (cur_node->children)
+            vboxSnapshotXmlRetrieveSnapshotNodeByName(cur_node->children, name, snap_node);
+    }
+}
+
+
+
+
+static int
+vboxDetachAndCloseDisks(virDomainPtr dom,
+                        IMedium *disk)
+{
+    VBOX_OBJECT_CHECK(dom->conn, int, -1);
+    nsresult rc;
+    PRUnichar *location = NULL;
+    vboxArray childrenDiskArray = VBOX_ARRAY_INITIALIZER;
+    virStorageVolPtr volPtr = NULL;
+    char *location_utf8 = NULL;
+    PRUint32 dummyState = 0;
+    size_t i = 0;
+    if (disk == NULL) {
+        VIR_DEBUG("Null pointer to disk");
+        return -1;
+    }
+    rc = disk->vtbl->GetLocation(disk, &location);
+    if (rc == VBOX_E_ACCESSDENIED) {
+        VIR_DEBUG("Disk already closed");
+        goto cleanup;
+    }
+    reportInternalErrorIfNS_FAILED("cannot get disk location");
+    rc = vboxArrayGet(&childrenDiskArray, disk, disk->vtbl->GetChildren);
+    reportInternalErrorIfNS_FAILED("cannot get children disks");
+    for (i = 0; i < childrenDiskArray.count; ++i) {
+        IMedium *childDisk = childrenDiskArray.items[i];
+        if (childDisk) {
+            vboxDetachAndCloseDisks(dom, childDisk);
+        }
+    }
+    rc = disk->vtbl->RefreshState(disk, &dummyState);
+    reportInternalErrorIfNS_FAILED("cannot refresh state");
+    VBOX_UTF16_TO_UTF8(location, &location_utf8);
+    volPtr = vboxStorageVolLookupByPath(dom->conn, location_utf8);
+
+    if (volPtr) {
+        VIR_DEBUG("Closing %s", location_utf8);
+        if (vboxStorageDeleteOrClose(volPtr, 0, VBOX_STORAGE_CLOSE_FLAG) != 0) {
+            VIR_DEBUG("Error while closing disk");
+        }
+    }
+    VBOX_UTF8_FREE(location_utf8);
+cleanup:
+    VBOX_UTF16_FREE(location);
+    vboxArrayRelease(&childrenDiskArray);
+    return ret;
+}
+
+static void
+vboxSnapshotXmlAddChild(xmlNodePtr parent,
+                xmlNodePtr child)
+{
+    /*Used in order to add child without writing the stuff concerning xml namespaces*/
+    xmlBufferPtr tmpBuf = xmlBufferCreate();
+    char *tmpString = NULL;
+    xmlNodePtr tmpNode = NULL;
+    xmlNodeDump(tmpBuf, parent->doc, child, 0, 0);
+    ignore_value(VIR_STRDUP(tmpString, (char *)xmlBufferContent(tmpBuf)));
+    xmlParseInNodeContext(parent, tmpString, (int)strlen(tmpString), 0, &tmpNode);
+    if (tmpNode) {
+        if (xmlAddChild(parent, xmlCopyNode(tmpNode, 1)) == NULL) {
+            VIR_DEBUG("Error while adding %s to %s", (char *)tmpNode->name, (char *)parent->name);
+        }
+    }
+    xmlFree(tmpNode);
+    xmlBufferFree(tmpBuf);
+}
+
+static void
+vboxSnapshotXmlRetrieveMachineNode(xmlNodePtr root,
+                        xmlNodePtr *machineNode)
+{
+    xmlNodePtr cur = root->xmlChildrenNode;
+    while (cur && xmlIsBlankNode(cur)) {
+        cur = cur -> next;
+    }
+    if (xmlStrcmp(cur->name, (const xmlChar *) "Machine")) {
+        VIR_DEBUG("Problem, found %s, Machine expected", (char *)cur->name);
+        return;
+    }
+    *machineNode = cur;
+}
+
+static void
+vboxSnapshotXmlRetrieveSnapshotsNode(xmlNodePtr snapshotNode,
+                            xmlNodePtr *snapshotsNode)
+{
+    xmlNodePtr cur_node = NULL;
+
+    for (cur_node = snapshotNode; cur_node; cur_node = cur_node->next) {
+        if (cur_node->type == XML_ELEMENT_NODE) {
+            if (!xmlStrcmp(cur_node->name, (const xmlChar *) "Snapshots")) {
+                *snapshotsNode = cur_node;
+                return;
+            }
+        }
+        if (cur_node->children)
+            vboxSnapshotXmlRetrieveSnapshotsNode(cur_node->children, snapshotsNode);
+    }
+}
+
+static void
+vboxSnapshotXmlRetrieveRootNodeByName(xmlNodePtr root,
+                           const char *name,
+                            xmlNodePtr *returnNode)
+{
+    xmlNodePtr cur = root->xmlChildrenNode;
+    while (cur && xmlIsBlankNode(cur)) {
+        cur = cur -> next;
+    }
+    if (xmlStrcmp(cur->name, (const xmlChar *) "Machine")) {
+        VIR_DEBUG("Problem, found %s, Machine expected", (char *)cur->name);
+    }
+    cur=cur->xmlChildrenNode;
+    while (cur != NULL) {
+        if (!xmlStrcmp(cur->name, (const xmlChar*) name)) {
+            *returnNode = cur;
+            return;
+        }
+        cur = cur -> next;
+    }
+}
+
+static void
+vboxSnapshotXmlAppendDiskToMediaRegistry(xmlNodePtr *inMediaRegistry,
+                              char *parentId,
+                              char *childId,
+                              char *childLocation,
+                              char *childFormat)
+{
+    /*This function will modify the inMediaregistry node to append all the informations about the child disk
+     */
+    xmlNodePtr cur_node = NULL;
+    for (cur_node = *inMediaRegistry; 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 "uuid") != NULL
+                && STREQ((char *)xmlHasProp(cur_node, BAD_CAST "uuid")->children->content, parentId)) {
+
+                xmlNodePtr childDiskNode = xmlNewTextChild(cur_node, NULL, BAD_CAST "HardDisk", NULL);
+                xmlNewProp(childDiskNode, BAD_CAST "uuid", BAD_CAST childId);
+                xmlNewProp(childDiskNode, BAD_CAST "location", BAD_CAST childLocation);
+                xmlNewProp(childDiskNode, BAD_CAST "format", BAD_CAST childFormat);
+                return;
+            }
+        }
+        if (&(cur_node->children))
+            vboxSnapshotXmlAppendDiskToMediaRegistry(&(cur_node->children), parentId, childId, childLocation, childFormat);
+
+    }
+}
+
+static int
+vboxSnapshotGenerateVboxXML(xmlNodePtr rootElementVboxXML,
+                            char *storageControllerString,
+                            char *parent,
+                            char *defName,
+                            char *timeStamp,
+                            char *uuid,
+                            xmlNodePtr *snapshotNodeReturned)
+{
+    xmlDocPtr doc;
+    xmlNodePtr snapshotNode;
+    xmlNodePtr snapshotsNode;
+    xmlNodePtr machineHardwareNode = NULL;
+    char *uuidstrWithBrackets = NULL;
+    char *timeVboxFormat = NULL;
+    int ret = -1;
+    /*We change the date format from "yyyy-MM-dd hh:mm:ss.msec+timeZone" to "yyyy-MM-ddThh:mm:ssZ"*/
+    char *date = virStringSplit(virStringJoin((const char**)virStringSplit(virStringSplit(timeStamp, "+",0)[0]," ",0),"T"), ".",0)[0];
+
+    /*Retrieve hardware*/
+    vboxSnapshotXmlRetrieveRootNodeByName(rootElementVboxXML, "Hardware", &machineHardwareNode);
+    /* Create a new XML DOM tree, to which the XML document will be
+         * written */
+    doc = xmlNewDoc(BAD_CAST XML_DEFAULT_VERSION);
+    if (doc == NULL) {
+        VIR_DEBUG("Error creating the xml document tree\n");
+        ret = -1;
+        goto cleanup;
+    }
+    /* Create a new XML node, to which the XML document will be
+         * appended */
+    snapshotNode = xmlNewDocNode(doc, NULL, BAD_CAST "Snapshot", NULL);
+    if (snapshotNode == NULL) {
+        VIR_DEBUG("Error creating the xml node Snapshot\n");
+        ret = -1;
+        goto cleanup;
+    }
+    if (virAsprintf(&uuidstrWithBrackets, "{%s}", uuid) != -1) {
+        xmlNewProp(snapshotNode, BAD_CAST "uuid", BAD_CAST uuidstrWithBrackets);
+        VIR_FREE(uuidstrWithBrackets);
+    }
+    xmlNewProp(snapshotNode, BAD_CAST "name", BAD_CAST defName);
+    if (virAsprintf(&timeVboxFormat, "%sZ", date) != -1) {
+        xmlNewProp(snapshotNode, BAD_CAST "timeStamp", BAD_CAST timeVboxFormat);
+        VIR_FREE(timeVboxFormat);
+    }
+    xmlDocSetRootElement(doc, snapshotNode);
+    if (machineHardwareNode) {
+        vboxSnapshotXmlAddChild(snapshotNode, machineHardwareNode);
+    }
+    xmlNodePtr listStorageControllerNodes;
+    xmlParseInNodeContext(snapshotNode, storageControllerString, (int)strlen(storageControllerString), 0, &listStorageControllerNodes);
+    if (listStorageControllerNodes) {
+        if (xmlAddChild(snapshotNode, xmlCopyNode(listStorageControllerNodes, 1)) == NULL) {
+            VIR_DEBUG("Could not add listStorageControllerNodes node to snapshot node");
+            ret = -1;
+            goto cleanup;
+        }
+    }
+    snapshotsNode = xmlNewDocNode(doc, NULL, BAD_CAST "Snapshots", NULL);
+    if (snapshotsNode == NULL) {
+        VIR_DEBUG("Error creating the xml node Snapshots\n");
+        ret = -1;
+        goto cleanup;
+    }
+    if (snapshotsNode) {
+        if (xmlAddChild(snapshotNode, xmlCopyNode(snapshotsNode, 1)) == NULL) {
+            VIR_DEBUG("Could not add snapshotsNode node to snapshot node");
+            ret = -1;
+            goto cleanup;
+        }
+    }
+    if (parent) {
+        xmlNodePtr rootSnapshotNode = NULL;
+        xmlNodePtr parentNode = NULL;
+        xmlNodePtr parentSnapshotsNode = NULL;
+        vboxSnapshotXmlRetrieveRootNodeByName(rootElementVboxXML, "Snapshot", &rootSnapshotNode);
+        if (!rootSnapshotNode) {
+            VIR_DEBUG("Could not retrieve Snapshot node");
+            ret = -1;
+            goto cleanup;
+        }
+        vboxSnapshotXmlRetrieveSnapshotNodeByName(rootSnapshotNode, parent, &parentNode);
+        if (!parentNode) {
+            VIR_DEBUG("Could not retrieve Snapshot node of %s", parent);
+            ret = -1;
+            goto cleanup;
+        }
+        vboxSnapshotXmlRetrieveSnapshotsNode(parentNode->children, &parentSnapshotsNode);
+        if (!parentSnapshotsNode) {
+            VIR_DEBUG("Could not retrieve Snapshots node");
+            ret = -1;
+            goto cleanup;
+        }
+        if (xmlAddChild(parentSnapshotsNode, xmlCopyNode(snapshotNode, 1)) == NULL) {
+            VIR_DEBUG("Could not add snapshotsNode node to its parent");
+            ret = -1;
+            goto cleanup;
+        } else {
+            *snapshotNodeReturned = xmlCopyNode(rootSnapshotNode, 1);
+        }
+    } else {
+        *snapshotNodeReturned = xmlCopyNode(snapshotNode,1);
+    }
+    ret = 0;
+cleanup:
+    return ret;
+}
+
+static xmlBufferPtr
+vboxSnapshotGenerateXmlSkeleton(char *domainUuid,
+                        char *machineName,
+                        char *snapshotUuid,
+                        char *snapshotFolder,
+                        PRInt64 lastStateChange,
+                        bool isCurrent,
+                        char *savedCurrentSnapshotUuid)
+{
+    char *snapshotUuidWithBrackets = NULL;
+    char *lastStateChangeStr = NULL;
+    /*Let's write a new xml
+        */
+    xmlTextWriterPtr writer;
+    xmlBufferPtr buf = xmlBufferCreate();
+    xmlBufferPtr ret = NULL;
+    int resultXml;
+    char *uuidWithBracket = NULL;
+
+    /* Create a new XmlWriter with no compression. */
+    writer = xmlNewTextWriterMemory(buf, 0);
+    if (writer == NULL) {
+        VIR_DEBUG("Error creating the xml writer\n");
+        goto cleanup;
+    }
+
+    /* Start the document with the xml default for the version,
+     * encoding ISO 8859-1 and the default for the standalone
+     * declaration. */
+    resultXml = xmlTextWriterStartDocument(writer, NULL, "ISO-8859-1", NULL);
+    if (resultXml < 0) {
+        VIR_DEBUG("Error at xmlTextWriterStartDocument\n");
+        goto cleanup;
+    }
+    /* Write a comment as child of root.
+     * Please observe, that the input to the xmlTextWriter functions
+     * HAS to be in UTF-8, even if the output XML is encoded
+     * in iso-8859-1 */
+    resultXml = xmlTextWriterWriteFormatComment(writer, "WARNING: THIS IS AN AUTO-GENERATED FILE. CHANGES TO IT ARE LIKELY TO BE \n"
+                                                "OVERWRITTEN AND LOST.\n"
+                                                "Changes to this xml configuration should be made using Virtualbox \n"
+                                                "or other application using the libvirt API");
+    if (resultXml < 0) {
+        VIR_DEBUG("Error at xmlTextWriterWriteComment\n");
+        goto cleanup;
+    }
+    xmlTextWriterWriteRaw(writer,BAD_CAST "\n"); /*Break line after comment*/
+
+    /* Start an element named "VirtualBox". Since thist is the first
+     * element, this will be the root element of the document. */
+    resultXml = xmlTextWriterStartElement(writer, BAD_CAST "VirtualBox");
+    if (resultXml < 0) {
+        VIR_DEBUG("Error creating the xml node\n");
+        goto cleanup;
+    }
+    resultXml = xmlTextWriterWriteAttribute(writer, BAD_CAST "version",BAD_CAST "1.12-linux");
+    if (resultXml < 0) {
+        VIR_DEBUG("Error at xmlTextWriterWriteAttribute\n");
+        goto cleanup;
+    }
+    /* Start an element named "Machine" as child of VirtualBox. */
+    resultXml = xmlTextWriterStartElement(writer, BAD_CAST "Machine");
+    if (resultXml < 0) {
+        VIR_DEBUG("Error at xmlTextWriterStartElement\n");
+        goto cleanup;
+    }
+    /* Add an attribute with name "uuid" and value "{uuid}" to Machine. */
+    if (virAsprintf(&uuidWithBracket,"{%s}",domainUuid) != -1) {
+        resultXml = xmlTextWriterWriteAttribute(writer, BAD_CAST "uuid",BAD_CAST uuidWithBracket);
+        if (resultXml < 0) {
+            VIR_DEBUG("Error at xmlTextWriterWriteAttribute\n");
+            goto cleanup;
+        }
+        VIR_FREE(uuidWithBracket);
+    }
+
+    if (!machineName) {
+        VIR_DEBUG("No machine name");
+        goto cleanup;
+    }
+    resultXml = xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST machineName);
+    if (resultXml < 0) {
+        VIR_DEBUG("Error at xmlTextWriterWriteAttribute\n");
+        goto cleanup;
+    }
+    resultXml = xmlTextWriterWriteAttribute(writer, BAD_CAST "OSType", BAD_CAST "Other");
+    if (resultXml < 0) {
+        VIR_DEBUG("Error at xmlTextWriterWriteAttribute\n");
+        goto cleanup;
+    }
+
+    if (!snapshotFolder) {
+        VIR_DEBUG("No machine snapshotFolder");
+        goto cleanup;
+    }
+    resultXml = xmlTextWriterWriteAttribute(writer, BAD_CAST "snapshotFolder", BAD_CAST snapshotFolder);
+    if (resultXml < 0) {
+        VIR_DEBUG("Error at xmlTextWriterWriteAttribute\n");
+        goto cleanup;
+    }
+    if (virAsprintf(&lastStateChangeStr, "%ld", lastStateChange) < 0) {
+        VIR_DEBUG("Could not convert lastStateChange from long to string");
+        goto cleanup;
+    }
+    resultXml = xmlTextWriterWriteAttribute(writer, BAD_CAST "lastStateChange", BAD_CAST lastStateChangeStr);
+    if (resultXml < 0) {
+        VIR_DEBUG("Error at xmlTextWriterWriteAttribute\n");
+        goto cleanup;
+    }
+    /* Current Snapshot
+     * https://www.virtualbox.org/sdkref/interface_i_machine.html#ac785dbe04eccc0793d949d6940202767
+     * There is always a current snapshot, except when there is no snapshot (obvious) or when they have all been deleted (obvious)
+     * or when the current snapshot is deleted and it does not have a parent (not so obvious).
+     * This can happen only when the last snapshot is deleted because there can only be one snapshot with no parent, the first one.
+     * For the moment we always put the snapshot we are defining as current snapshot. When all the snapshots are redefined, you can always set
+     * the current snapshot to one you have previously saved.
+     */
+    if (isCurrent) {
+        if (virAsprintf(&snapshotUuidWithBrackets, "{%s}", snapshotUuid) != -1) {
+            resultXml = xmlTextWriterWriteAttribute(writer, BAD_CAST "currentSnapshot", BAD_CAST snapshotUuidWithBrackets);
+            if (resultXml < 0) {
+                VIR_DEBUG("Error at xmlTextWriterWriteAttribute\n");
+                goto cleanup;
+            }
+            VIR_FREE(snapshotUuidWithBrackets);
+        }
+    }
+    if (savedCurrentSnapshotUuid != NULL) {
+        if (virAsprintf(&snapshotUuidWithBrackets, "{%s}", savedCurrentSnapshotUuid) != -1) {
+            resultXml = xmlTextWriterWriteAttribute(writer, BAD_CAST "currentSnapshot", BAD_CAST snapshotUuidWithBrackets);
+            if (resultXml < 0) {
+                VIR_DEBUG("Error at xmlTextWriterWriteAttribute\n");
+                goto cleanup;
+            }
+            VIR_FREE(snapshotUuidWithBrackets);
+        }
+    }
+
+    /* Here we could close the elements Machine and VirtualBox using the
+     * function xmlTextWriterEndElement, but since we do not want to
+     * write any other elements, we simply call xmlTextWriterEndDocument,
+     * which will do all the work. */
+    resultXml = xmlTextWriterEndDocument(writer);
+    if (resultXml < 0) {
+        VIR_DEBUG("Error at xmlTextWriterEndDocument\n");
+        goto cleanup;
+    }
+    xmlFreeTextWriter(writer);
+    /* Now we have a file containing the skeleton for the xml we want to build
+     * It should look like this:
+     * <?xml version="1.0" encoding="ISO-8859-1"?>
+     * <!--comment
+     * -->
+     * <VirtualBox>
+     *  <Machine uuid="{anUuid}" name="the name" OSType="Other" snapshotFolder="/path/to/SnapshotFolder" lastStateChange="a date"/>
+     * </VirtualBox>
+     */
+     ret = buf;
+cleanup:
+    return ret;
+}
+
+static int
+vboxSnapshotRedefine(virDomainPtr dom,
+                     virDomainSnapshotDefPtr def,
+                     bool isCurrent)
+{
+    /*
+     * If your snapshot has a parent,
+     * it will only be redefined if you have already
+     * redefined the parent.
+     *
+     */
+
+    VBOX_OBJECT_CHECK(dom->conn, int, 0);
+    vboxIID domiid = VBOX_IID_INITIALIZER;
+    IMachine *machine = NULL;
+    nsresult rc;
+    PRUnichar *settingsFilePath = NULL;
+    char *settingsFilePath_Utf8 = NULL;
+    size_t it = 0;
+
+    IMachine *newMachine = NULL;
+    xmlDocPtr xml = NULL;
+    bool snapshotHasParents = !(def->parent == NULL);
+
+    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"));
+        ret = -1;
+        goto cleanup;
+    }
+
+
+    rc = machine->vtbl->SaveSettings(machine);
+    rc = machine->vtbl->GetSettingsFilePath(machine, &settingsFilePath);
+    reportInternalErrorIfNS_FAILED("cannot get settings file path");
+    VBOX_UTF16_TO_UTF8(settingsFilePath, &settingsFilePath_Utf8);
+    xml = virXMLParse(settingsFilePath_Utf8, NULL, NULL);
+    if (xml) {
+        xmlNodePtr cur = NULL;
+        xmlNodePtr machineHardwareNode = NULL;
+        xmlNodePtr extraDataNode = NULL;
+        xmlNodePtr mediaRegistryNodeOriginal = NULL;
+        xmlNodePtr snapshotStorageControllersNode = NULL;
+        xmlNodePtr modifiedMachineStorageControllersNode = NULL;
+        char *newMachineStorageControllersString = NULL;
+        char *newSnapshotStorageControllersString = NULL;
+        char snapshotuuidstr[VIR_UUID_STRING_BUFLEN];
+        unsigned char snapshotUuid[VIR_UUID_BUFLEN];
+
+        char *domainUuid = NULL;
+        PRUnichar *machineNameUtf16 = NULL;
+        char *machineName = NULL;
+        PRUnichar *snapshotFolderUtf16 = NULL;
+        PRInt64 lastStateChange = 0;
+        ISnapshot *savedCurrentSnapshot = NULL;
+        vboxIID savedCurrentSnapshotIid = VBOX_IID_INITIALIZER;
+        char *savedCurrentSnapshotUuid = NULL;
+        cur = xmlDocGetRootElement(xml);
+        xmlBufferPtr buf = xmlBufferCreate();
+        if (cur == NULL) {
+            VIR_DEBUG("empty document\n");
+            ret = -1;
+            goto cleanup;
+        }
+        /*Retrieve hardware*/
+        vboxSnapshotXmlRetrieveRootNodeByName(cur, "Hardware", &machineHardwareNode);
+        if (!machineHardwareNode) {
+            VIR_DEBUG("Could not retrieve Hardware node");
+            ret = -1;
+            goto cleanup;
+        }
+        /*Retrieve storageController*/
+        buf = xmlBufferCreate();
+        vboxSnapshotXmlRetrieveRootNodeByName(cur, "StorageControllers", &snapshotStorageControllersNode);
+        if (!snapshotStorageControllersNode) {
+            VIR_DEBUG("Could not retrieve StorageControllers node");
+            ret = -1;
+            goto cleanup;
+        }
+        xmlNodeDump(buf, xml, snapshotStorageControllersNode, 0, 0);
+        ignore_value(VIR_STRDUP(newMachineStorageControllersString, (char *)xmlBufferContent(buf)));
+        ignore_value(VIR_STRDUP(newSnapshotStorageControllersString, newMachineStorageControllersString));
+        xmlBufferFree(buf);
+        /*Retrieve ExtraData*/
+        vboxSnapshotXmlRetrieveRootNodeByName(cur, "ExtraData", &extraDataNode);
+        if (!extraDataNode) {
+            VIR_DEBUG("Could not retrieve ExtraData node");
+            ret = -1;
+            goto cleanup;
+        }
+        /*Retrieve MediaRegistry*/
+        vboxSnapshotXmlRetrieveRootNodeByName(cur, "MediaRegistry", &mediaRegistryNodeOriginal);
+        if (!mediaRegistryNodeOriginal) {
+            VIR_DEBUG("Could not retrieve MediaRegistry node");
+            ret = -1;
+            goto cleanup;
+        }
+        vboxArray mediumAttachments         = VBOX_ARRAY_INITIALIZER;
+        rc = vboxArrayGet(&mediumAttachments, machine, machine->vtbl->GetMediumAttachments);
+        reportInternalErrorIfNS_FAILED("cannot get medium attachments");
+
+        /*Read only disks*/
+        for (it = 0; it<def->dom->ndisks; ++it) {
+            IMedium *mediumReadOnly = NULL;
+            PRUnichar *locationReadOnlyDiskUtf16 = NULL;
+            PRUnichar *mediumReadOnlyFormatUtf16 = NULL;
+            PRUnichar *mediumReadOnlyIdUtf16 = NULL;
+            char *mediumReadOnlyFormat = NULL;
+            char *mediumReadOnlyId = NULL;
+            char *mediaRegistryNodeOriginalString = NULL;
+            VBOX_UTF8_TO_UTF16(def->dom->disks[it]->src, &locationReadOnlyDiskUtf16);
+            data->vboxObj->vtbl->OpenMedium(data->vboxObj, locationReadOnlyDiskUtf16, DeviceType_HardDisk, AccessMode_ReadWrite, false, &mediumReadOnly);
+            if (mediumReadOnly) {
+                rc = mediumReadOnly->vtbl->GetId(mediumReadOnly, &mediumReadOnlyIdUtf16);
+                reportInternalErrorIfNS_FAILED("cannot get medium id");
+                rc = mediumReadOnly->vtbl->GetFormat(mediumReadOnly, &mediumReadOnlyFormatUtf16);
+                reportInternalErrorIfNS_FAILED("cannot get medium format");
+                VBOX_UTF16_TO_UTF8(mediumReadOnlyIdUtf16, &mediumReadOnlyId);
+                VBOX_UTF16_TO_UTF8(mediumReadOnlyFormatUtf16, &mediumReadOnlyFormat);
+                char *uuidToReplace = NULL;
+                virSearchUuid(newSnapshotStorageControllersString, it+1,&uuidToReplace);
+                if (STRNEQ(uuidToReplace, mediumReadOnlyId)) {
+                    char *tmp = NULL;
+                    tmp = virStrReplace(newSnapshotStorageControllersString, uuidToReplace, mediumReadOnlyId);
+                    VIR_FREE(newSnapshotStorageControllersString);
+                    if (VIR_STRDUP(newSnapshotStorageControllersString, tmp) < 0) {
+                        VIR_DEBUG("Error while using strdup");
+                    }
+                    VIR_FREE(tmp);
+                }
+                IMedium *mediumReadOnlyParent = NULL;
+                rc = mediumReadOnly->vtbl->GetParent(mediumReadOnly, &mediumReadOnlyParent);
+                reportInternalErrorIfNS_FAILED("cannot get medium parent");
+                if (mediumReadOnlyParent) {
+                    PRUnichar *parentIdUtf16 = NULL;
+                    char *parentId = NULL;
+                    char *parentIdWithBrackets = NULL;
+                    char *childIdWithBrackets = NULL;
+                    rc = mediumReadOnlyParent->vtbl->GetId(mediumReadOnlyParent, &parentIdUtf16);
+                    reportInternalErrorIfNS_FAILED("cannot get medium id");
+                    if (parentIdUtf16) {
+                        VBOX_UTF16_TO_UTF8(parentIdUtf16, &parentId);
+                        if (virAsprintf(&parentIdWithBrackets, "{%s}", parentId) != -1 && virAsprintf(&childIdWithBrackets, "{%s}", mediumReadOnlyId) != -1) {
+                            buf = xmlBufferCreate();
+                            xmlNodeDump(buf, mediaRegistryNodeOriginal->doc, mediaRegistryNodeOriginal, 0, 0);
+                            ignore_value(VIR_STRDUP(mediaRegistryNodeOriginalString, (char *)xmlBufferContent(buf)));
+                            if (strstr(mediaRegistryNodeOriginalString, childIdWithBrackets) == NULL) {
+                                vboxSnapshotXmlAppendDiskToMediaRegistry(&mediaRegistryNodeOriginal->children, parentIdWithBrackets, childIdWithBrackets, def->dom->disks[it]->src, mediumReadOnlyFormat);
+                            } else {
+                                VIR_DEBUG("Already added");
+                            }
+                            VIR_FREE(parentIdWithBrackets);
+                            VIR_FREE(childIdWithBrackets);
+                        }
+                        VBOX_UTF8_FREE(parentId);
+                    }
+                    VBOX_RELEASE(mediumReadOnlyParent);
+                    VBOX_UTF16_FREE(parentIdUtf16);
+                }
+                VBOX_UTF8_FREE(mediumReadOnlyFormat);
+                VBOX_UTF8_FREE(mediumReadOnlyId);
+                VBOX_RELEASE(mediumReadOnly);
+            }
+            VBOX_UTF16_FREE(mediumReadOnlyIdUtf16);
+            VBOX_UTF16_FREE(mediumReadOnlyFormatUtf16);
+            VBOX_UTF16_FREE(locationReadOnlyDiskUtf16);
+        }
+
+        /*Read Write disks*/
+        for (it = 0; it< def->ndisks; ++it) {
+            IMedium *mediumReadWrite = NULL;
+            PRUnichar *mediumReadWriteFormatUtf16 = NULL;
+            PRUnichar *mediumReadWriteIdUtf16 = NULL;
+            PRUnichar *locationReadWriteDiskUtf16 = NULL;
+            char *mediumReadWriteFormat = NULL;
+            char *mediumReadWriteId = NULL;
+            char *mediaRegistryNodeOriginalString = NULL;
+            VBOX_UTF8_TO_UTF16(def->disks[it].file, &locationReadWriteDiskUtf16);
+            rc = data->vboxObj->vtbl->OpenMedium(data->vboxObj, locationReadWriteDiskUtf16, DeviceType_HardDisk, AccessMode_ReadWrite, false, &mediumReadWrite);
+            if (NS_FAILED(rc)) {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s"
+                               "%s",
+                               _("Could not open medium"),def->disks[it].file);
+                ret = -1;
+                goto cleanup;
+            }
+            if (mediumReadWrite) {
+                rc = mediumReadWrite->vtbl->GetId(mediumReadWrite, &mediumReadWriteIdUtf16);
+                reportInternalErrorIfNS_FAILED("cannot get medium id");
+                rc = mediumReadWrite->vtbl->GetFormat(mediumReadWrite, &mediumReadWriteFormatUtf16);
+                reportInternalErrorIfNS_FAILED("cannot get medium format");
+                VBOX_UTF16_TO_UTF8(mediumReadWriteIdUtf16, &mediumReadWriteId);
+                VBOX_UTF16_TO_UTF8(mediumReadWriteFormatUtf16, &mediumReadWriteFormat);
+                char *uuidToReplace = NULL;
+                virSearchUuid(newMachineStorageControllersString, it+1,&uuidToReplace);
+                if (STRNEQ(uuidToReplace, mediumReadWriteId)) {
+                    char *tmp = NULL;
+                    tmp = virStrReplace(newMachineStorageControllersString, uuidToReplace, mediumReadWriteId);
+                    VIR_FREE(newMachineStorageControllersString);
+                    if (VIR_STRDUP(newMachineStorageControllersString, tmp) < 0) {
+                        VIR_DEBUG("Error while using strdup");
+                    }
+                    VIR_FREE(tmp);
+                }
+                buf = xmlBufferCreate();
+                xmlNodeDump(buf, mediaRegistryNodeOriginal->doc, mediaRegistryNodeOriginal, 0, 0);
+                ignore_value(VIR_STRDUP(mediaRegistryNodeOriginalString, (char *)xmlBufferContent(buf)));
+                xmlBufferFree(buf);
+                if (mediaRegistryNodeOriginalString && (strstr(mediaRegistryNodeOriginalString, mediumReadWriteId) == NULL)) {
+                    IMedium *mediumReadWriteParent = NULL;
+                    rc = mediumReadWrite->vtbl->GetParent(mediumReadWrite, &mediumReadWriteParent);
+                    reportInternalErrorIfNS_FAILED("cannot get disk parent");
+                    if (mediumReadWriteParent) {
+                        PRUnichar *parentIdUtf16 = NULL;
+                        char *parentId = NULL;
+                        char *parentIdWithBrackets = NULL;
+                        char *childIdWithBrackets = NULL;
+                        rc = mediumReadWriteParent->vtbl->GetId(mediumReadWriteParent, &parentIdUtf16);
+                        reportInternalErrorIfNS_FAILED("cannot get disk id");
+                        if (parentIdUtf16) {
+                            VBOX_UTF16_TO_UTF8(parentIdUtf16, &parentId);
+                            if (virAsprintf(&parentIdWithBrackets, "{%s}", parentId) != -1 && virAsprintf(&childIdWithBrackets, "{%s}", mediumReadWriteId) != -1) {
+                                vboxSnapshotXmlAppendDiskToMediaRegistry(&(mediaRegistryNodeOriginal->children), parentIdWithBrackets, childIdWithBrackets, def->disks[it].file, mediumReadWriteFormat);
+                                VIR_FREE(parentIdWithBrackets);
+                                VIR_FREE(childIdWithBrackets);
+                            }
+                            VBOX_UTF8_FREE(parentId);
+                        }
+                        VBOX_RELEASE(mediumReadWriteParent);
+                        VBOX_UTF16_FREE(parentIdUtf16);
+                    }
+                }
+                VBOX_UTF8_FREE(mediumReadWriteFormat);
+                VBOX_UTF8_FREE(mediumReadWriteId);
+                VBOX_RELEASE(mediumReadWrite);
+            }
+            VBOX_UTF16_FREE(mediumReadWriteIdUtf16);
+            VBOX_UTF16_FREE(mediumReadWriteFormatUtf16);
+            VBOX_UTF16_FREE(locationReadWriteDiskUtf16);
+        }
+        vboxArrayRelease(&mediumAttachments);
+
+        /*Generation of the skeleton*/
+
+        VBOX_UTF16_TO_UTF8((PRUnichar *)domiid.value, &domainUuid);
+        rc = machine->vtbl->GetName(machine, &machineNameUtf16);
+        reportInternalErrorIfNS_FAILED("cannot get machine name");
+        rc = machine->vtbl->GetSnapshotFolder(machine, &snapshotFolderUtf16);
+        reportInternalErrorIfNS_FAILED("cannot get snapshot folder");
+        char *snapshotFolder = NULL;
+        VBOX_UTF16_TO_UTF8(snapshotFolderUtf16, &snapshotFolder);
+        VBOX_UTF16_FREE(snapshotFolderUtf16);
+        VBOX_UTF16_TO_UTF8(machineNameUtf16, &machineName);
+        VBOX_UTF16_FREE(machineNameUtf16);
+        if (snapshotUuid == NULL) {
+            VIR_DEBUG("Bad initialization");
+        }
+        if (virUUIDGenerate(snapshotUuid) == -1) {
+            VIR_DEBUG("Could not generate a snapshot uuid");
+        } else {
+            virUUIDFormat(snapshotUuid, snapshotuuidstr);
+            VIR_DEBUG("UUID: %s", snapshotuuidstr);
+        }
+        rc = machine->vtbl->GetCurrentSnapshot(machine, &savedCurrentSnapshot);
+        reportInternalErrorIfNS_FAILED("cannot get current snapshot");
+        if (savedCurrentSnapshot) {
+            savedCurrentSnapshot->vtbl->GetId(savedCurrentSnapshot, &savedCurrentSnapshotIid.value);
+            VBOX_UTF16_TO_UTF8(savedCurrentSnapshotIid.value, &savedCurrentSnapshotUuid);
+            vboxIIDUnalloc(&savedCurrentSnapshotIid);
+            VBOX_RELEASE(savedCurrentSnapshot);
+        }
+        rc = machine ->vtbl->GetLastStateChange(machine, &lastStateChange);
+        reportInternalErrorIfNS_FAILED("cannot get last state change");
+        xmlBufferPtr skeletonBuf = vboxSnapshotGenerateXmlSkeleton(domainUuid, machineName, snapshotuuidstr, snapshotFolder, lastStateChange, isCurrent, savedCurrentSnapshotUuid);
+        /* Now we can reopen our file in tree mode and append node we want. */
+        xmlDocPtr newXml = virXMLParse(NULL, (char *)skeletonBuf->content, NULL);
+        if (newXml) {
+            xmlNodePtr newXmlRootElement = xmlDocGetRootElement(newXml);
+            xmlNodePtr newXmlMachineNode = NULL;
+            xmlNodePtr snapshotNodeToAppend = NULL;
+            IMedium *array[] = { NULL };
+            IProgress *progress = NULL;
+            vboxArray media = VBOX_ARRAY_INITIALIZER;
+
+            vboxSnapshotXmlRetrieveMachineNode(newXmlRootElement, &newXmlMachineNode);
+            if (newXmlMachineNode) {
+                if (mediaRegistryNodeOriginal) {
+                    vboxSnapshotXmlAddChild(newXmlMachineNode, mediaRegistryNodeOriginal);
+                }
+                if (extraDataNode) {
+                    vboxSnapshotXmlAddChild(newXmlMachineNode, extraDataNode);
+                }
+            }
+            if (!snapshotHasParents) {
+                vboxSnapshotGenerateVboxXML(xmlDocGetRootElement(xml), newSnapshotStorageControllersString , NULL,def->name,virTimeStringThen(def->creationTime*1000),snapshotuuidstr, &snapshotNodeToAppend);
+            } else {
+                vboxSnapshotGenerateVboxXML(xmlDocGetRootElement(xml), newSnapshotStorageControllersString , def->parent,def->name,virTimeStringThen(def->creationTime*1000),snapshotuuidstr, &snapshotNodeToAppend);
+            }
+            if (snapshotNodeToAppend) {
+                if (xmlAddChild(newXmlMachineNode, xmlCopyNode(snapshotNodeToAppend, 1)) == NULL) {
+                    VIR_DEBUG("Error while building the xml");
+                }
+            }
+            if (newXmlMachineNode) {
+                if (machineHardwareNode) {
+                    vboxSnapshotXmlAddChild(newXmlMachineNode, machineHardwareNode);
+                }
+                xmlParseInNodeContext(newXmlMachineNode, newMachineStorageControllersString, (int)strlen(newMachineStorageControllersString), 0, &modifiedMachineStorageControllersNode);
+                if (modifiedMachineStorageControllersNode) {
+                    if (xmlAddChild(newXmlMachineNode, xmlCopyNode(modifiedMachineStorageControllersNode, 1)) == NULL) {
+                        VIR_DEBUG("Could not add modifiedSnapshotStorageControllersNode node to snapshot node");
+                    }
+                }
+            }
+            xmlNewProp(newXmlRootElement, BAD_CAST "xmlns", BAD_CAST "http://www.innotek.de/VirtualBox-settings");
+            rc = vboxArrayGetWithUintArg(&media, machine, machine->vtbl->Unregister, CleanupMode_DetachAllReturnHardDisksOnly);
+            for (it = 0; it < media.count; ++it) {
+                IMedium *disk = media.items[it];
+                if (disk)
+                    vboxDetachAndCloseDisks(dom, disk);
+            }
+            rc = machine->vtbl->Delete(machine, 0, array, &progress);
+            reportInternalErrorIfNS_FAILED("cannot delete machine");
+            if (progress != NULL) {
+                progress->vtbl->WaitForCompletion(progress, -1);
+                VBOX_RELEASE(progress);
+            }
+            vboxArrayRelease(&media);
+            if (virFileMakePath(mdir_name(settingsFilePath_Utf8)) < 0)
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s"
+                               " %s",
+                               _("Cannot create path :"),
+                               settingsFilePath_Utf8);
+            if (xmlSaveFormatFileEnc(settingsFilePath_Utf8, newXml, "ISO-8859-1", 1) <= 0) {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s"
+                               " %s",
+                               _("Cannot save xml file to:"),
+                               settingsFilePath_Utf8);
+            }
+            rc = data->vboxObj->vtbl->OpenMachine(data->vboxObj, settingsFilePath, &newMachine);
+            unsigned int failedCounter = 10;
+            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:
+    xmlFreeDoc(xml);
+    return ret;
+}
+#endif
 static virDomainSnapshotPtr
 vboxDomainSnapshotCreateXML(virDomainPtr dom,
                             const char *xmlDesc,
@@ -5917,20 +6743,20 @@ vboxDomainSnapshotCreateXML(virDomainPtr dom,
 #else
     PRInt32 result;
 #endif
+#if VBOX_API_VERSION >= 4002
+    bool isCurrent = false;
+#endif
 
     /* VBox has no snapshot metadata, so this flag is trivial.  */
-    virCheckFlags(VIR_DOMAIN_SNAPSHOT_CREATE_NO_METADATA, NULL);
+    virCheckFlags(VIR_DOMAIN_SNAPSHOT_CREATE_NO_METADATA |
+                  VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE |
+                  VIR_DOMAIN_SNAPSHOT_CREATE_CURRENT, NULL);
 
     if (!(def = virDomainSnapshotDefParseString(xmlDesc, data->caps,
                                                 data->xmlopt, -1, VIR_DOMAIN_SNAPSHOT_PARSE_DISKS |
                                                                   VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE)))
         goto cleanup;
 
-    if (def->ndisks) {
-        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
-                       _("disk snapshots not supported yet"));
-        goto cleanup;
-    }
     vboxIIDFromUUID(&domiid, dom->uuid);
     rc = VBOX_OBJECT_GET_MACHINE(domiid.value, &machine);
     if (NS_FAILED(rc)) {
@@ -5938,6 +6764,14 @@ vboxDomainSnapshotCreateXML(virDomainPtr dom,
                        _("no domain with matching UUID"));
         goto cleanup;
     }
+#if VBOX_API_VERSION >= 4002
+    isCurrent = flags & VIR_DOMAIN_SNAPSHOT_CREATE_CURRENT;
+    if (flags & VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE) {
+        vboxSnapshotRedefine(dom, def, isCurrent);
+        ret = virGetDomainSnapshot(dom, def->name);
+        goto cleanup;
+    }
+#endif
     rc = machine->vtbl->GetState(machine, &state);
     if (NS_FAILED(rc)) {
         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
@@ -7189,8 +8023,6 @@ cleanup:
     return ret;
 }
 
-
-
 static int
 vboxDomainSnapshotDelete(virDomainSnapshotPtr snapshot,
                          unsigned int flags)
-- 
1.7.10.4




More information about the libvir-list mailing list