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 | 448 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 447 insertions(+), 1 deletion(-) diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c index a43a875..009ff3a 100644 --- a/src/vbox/vbox_tmpl.c +++ b/src/vbox/vbox_tmpl.c @@ -6171,6 +6171,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, @@ -8407,8 +8469,377 @@ 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) + goto cleanup; + + rc = vboxArrayGet(&childrenDiskArray, baseDisk, baseDisk->vtbl->GetChildren); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("could not get children disks")); + 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")); + 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); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot get parent snapshot")); + goto cleanup; + } + if (parentSnapshot) { + rc = parentSnapshot->vtbl->GetId(parentSnapshot, &parentIId.value); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot get parent snapshot id")); + goto cleanup; + } + 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); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot get settings file path")); + goto cleanup; + } + + /*We ask libxml2 to parse the xmlfile*/ + VBOX_UTF16_TO_UTF8(settingsFilePath, &settingsFilePathUtf8); + xml = virXMLParse(settingsFilePathUtf8, NULL, NULL); + xmlKeepBlanksDefault(1); + if (!xml) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot parse settings file")); + goto cleanup; + } + + xmlRootElement = xmlDocGetRootElement(xml); + if (!xmlRootElement) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot find any root element in the xml")); + goto cleanup; + } + + vboxSnapshotXmlRetrieveSnapshotNodeByName(xmlRootElement->children, + snapshot->name, + &snapshotToDelete); + if (!snapshotToDelete) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot find any snapshot to delete")); + 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) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("problem while using virasprintf")); + 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) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("problem while retrieving MediaRegistry node")); + goto cleanup; + } + vboxSnapshotXmlRetrieveRootNodeByName(xmlRootElement, "StorageControllers", &storageControllersNode); + if (!storageControllersNode) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("problem while retrieving StorageControllers node")); + goto cleanup; + } + xmlNodeDump(buf, xml, storageControllersNode, 0, 0); + if (VIR_STRDUP(snapshotStorageControllersString, (char *)xmlBufferContent(buf)) < 0) + goto cleanup; + + 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); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot open medium")); + goto cleanup; + } + if (!diskSnapshot) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("could not open: %s"), + def->dom->disks[i]->src); + goto cleanup; + } + rc = diskSnapshot->vtbl->GetBase(diskSnapshot, &baseDiskSnapshot); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot get base disk")); + goto cleanup; + } + if (!baseDiskSnapshot) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("could not get base medium")); + goto cleanup; + } + rc = baseDiskSnapshot->vtbl->GetId(baseDiskSnapshot, &baseDiskIdUtf16); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot get base disk id")); + goto cleanup; + } + 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); + virSearchRegex(snapshotStorageControllersString, + i, + "([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})", + &uuidToReplace); + char *tmp = NULL; + tmp = virStrReplace(snapshotStorageControllersString, uuidToReplace, baseDiskId); + VIR_FREE(snapshotStorageControllersString); + if (!tmp) + goto cleanup; + if (VIR_STRDUP(snapshotStorageControllersString, tmp) < 0) { + VIR_FREE(tmp); + goto cleanup; + } + 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); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot unregister machine")); + goto cleanup; + } +# if VBOX_API_VERSION == 4002 + rc = machine->vtbl->Delete(machine, 0, dummyArray, &progress); +# elif VBOX_API_VERSION >= 4003 + rc = machine->vtbl->DeleteConfig(machine, 0, dummyArray, &progress); +# endif + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot delete machine")); + goto cleanup; + } + 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); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot open medium")); + goto cleanup; + } + rc = diskSnapshot->vtbl->GetBase(diskSnapshot, &baseDiskSnapshot); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot get disk base")); + goto cleanup; + } + 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--; + } + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot open machine")); + goto cleanup; + } + if (newMachine) { + rc = data->vboxObj->vtbl->RegisterMachine(data->vboxObj, newMachine); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot register machine")); + goto cleanup; + } + } + else + goto cleanup; + + ret = 0; +cleanup: + vboxArrayRelease(&machineDisks); + VBOX_UTF16_FREE(settingsFilePath); + return ret; + +} +#endif +static int vboxDomainSnapshotDelete(virDomainSnapshotPtr snapshot, unsigned int flags) { @@ -8420,6 +8851,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); @@ -8447,7 +8879,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 >= 4002 + ret = vboxDomainSnapshotDeleteMetadataOnly(snapshot); +#endif + } goto cleanup; } -- 1.7.10.4 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list