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 | 453 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 452 insertions(+), 1 deletion(-) diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c index f8667f6..c5b5074 100644 --- a/src/vbox/vbox_tmpl.c +++ b/src/vbox/vbox_tmpl.c @@ -8347,7 +8347,443 @@ 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; + vboxSnapshotXmlMachinePtr 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 = vboxSnapshotLoadVboxFile(settingsFilepath, machineLocationPath); + if (!snapshotMachineDesc) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot create a vboxSnapshotXmlPtr")); + goto cleanup; + } + + isCurrent = isCurrentSnapshot(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++) { + vboxSnapshotXmlHardDiskPtr 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; + vboxSnapshotXmlHardDiskPtr disk = NULL; + PRUnichar *uuidUtf16 = NULL; + char *uuid = NULL; + char *format = NULL; + char **searchResultTab = NULL; + ssize_t resultSize = 0; + char *tmp = NULL; + + readOnly = hardDiskPtrByLocation(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, "%s %x" + , _("Unable to open HardDisk"), rc); + goto cleanup; + } + + rc = medium->vtbl->GetId(medium, &parentUuidUtf16); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s %x" + , _("Unable to get hardDisk Id"), 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, "%s %x" + , _("Unable to create HardDisk"), 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, "%s %x" + , _("Error while creating diff storage"), 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, "%s %x" + , _("Unable to get medium uuid"), 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 (addHardDiskToMediaRegistry(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, "%s %x" + , _("Unable to close the new medium"), 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 = hardDiskUuidByLocation(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 = hardDiskUuidByLocation(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 (removeHardDisk(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 = hardDiskUuidByLocation(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 (removeHardDisk(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, "%s %x" + , _("Unable to unregiter machine"), 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, "%s %x" + , _("Unable to delete medium"), rc); + goto cleanup; + } + progress->vtbl->WaitForCompletion(progress, -1); + progress->vtbl->GetResultCode(progress, &resultCode); + if (NS_FAILED(resultCode)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s %x" + , _("Error while closing medium"), 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 (removeSnapshot(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) { + vboxSnapshotXmlSnapshotPtr snap = snapshotByName(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 (vboxSnapshotSaveVboxFile(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, "%s %x" + , _("Unable to open Machine"), rc); + goto cleanup; + } + + rc = data->vboxObj->vtbl->RegisterMachine(data->vboxObj, machine); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s %x" + , _("Unable to register Machine"), 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, @@ -8361,6 +8797,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); @@ -8388,7 +8825,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 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list