mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-02-22 11:22:23 +00:00
vbox: Rewrite vboxDomainSnapshotDelete
This commit is contained in:
parent
a9725126bf
commit
4fab8d3f07
@ -6512,3 +6512,612 @@ int vboxDomainRevertToSnapshot(virDomainSnapshotPtr snapshot,
|
||||
vboxIIDUnalloc(&domiid);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
vboxDomainSnapshotDeleteSingle(vboxGlobalData *data,
|
||||
IConsole *console,
|
||||
ISnapshot *snapshot)
|
||||
{
|
||||
IProgress *progress = NULL;
|
||||
vboxIIDUnion iid;
|
||||
int ret = -1;
|
||||
nsresult rc;
|
||||
resultCodeUnion result;
|
||||
|
||||
VBOX_IID_INITIALIZE(&iid);
|
||||
rc = gVBoxAPI.UISnapshot.GetId(snapshot, &iid);
|
||||
if (NS_FAILED(rc)) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("could not get snapshot UUID"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
rc = gVBoxAPI.UIConsole.DeleteSnapshot(console, &iid, &progress);
|
||||
if (NS_FAILED(rc) || !progress) {
|
||||
if (rc == VBOX_E_INVALID_VM_STATE) {
|
||||
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
|
||||
_("cannot delete domain snapshot for running domain"));
|
||||
} else {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("could not delete snapshot"));
|
||||
}
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
gVBoxAPI.UIProgress.WaitForCompletion(progress, -1);
|
||||
gVBoxAPI.UIProgress.GetResultCode(progress, &result);
|
||||
if (RC_FAILED(result)) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("could not delete snapshot"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
cleanup:
|
||||
VBOX_RELEASE(progress);
|
||||
vboxIIDUnalloc(&iid);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
vboxDomainSnapshotDeleteTree(vboxGlobalData *data,
|
||||
IConsole *console,
|
||||
ISnapshot *snapshot)
|
||||
{
|
||||
vboxArray children = VBOX_ARRAY_INITIALIZER;
|
||||
int ret = -1;
|
||||
nsresult rc;
|
||||
size_t i;
|
||||
|
||||
rc = gVBoxAPI.UArray.vboxArrayGet(&children, snapshot,
|
||||
gVBoxAPI.UArray.handleSnapshotGetChildren(snapshot));
|
||||
if (NS_FAILED(rc)) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("could not get children snapshots"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
for (i = 0; i < children.count; i++) {
|
||||
if (vboxDomainSnapshotDeleteTree(data, console, children.items[i]))
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = vboxDomainSnapshotDeleteSingle(data, console, snapshot);
|
||||
|
||||
cleanup:
|
||||
gVBoxAPI.UArray.vboxArrayRelease(&children);
|
||||
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 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;
|
||||
vboxIIDUnion domiid;
|
||||
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;
|
||||
|
||||
VBOX_IID_INITIALIZE(&domiid);
|
||||
if (!gVBoxAPI.vboxSnapshotRedefine)
|
||||
VIR_WARN("This function may not work in current version");
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (openSessionForMachine(data, dom->uuid, &domiid, &machine, false) < 0)
|
||||
goto cleanup;
|
||||
rc = gVBoxAPI.UIMachine.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 = gVBoxAPI.UIMachine.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;
|
||||
char *parentUuid = NULL;
|
||||
IMedium *newMedium = NULL;
|
||||
PRUnichar *formatUtf16 = NULL;
|
||||
PRUnichar *newLocation = NULL;
|
||||
char *newLocationUtf8 = NULL;
|
||||
IProgress *progress = NULL;
|
||||
virVBoxSnapshotConfHardDiskPtr disk = NULL;
|
||||
char *uuid = NULL;
|
||||
char *format = NULL;
|
||||
char **searchResultTab = NULL;
|
||||
ssize_t resultSize = 0;
|
||||
char *tmp = NULL;
|
||||
vboxIIDUnion iid, parentiid;
|
||||
resultCodeUnion resultCode;
|
||||
|
||||
VBOX_IID_INITIALIZE(&iid);
|
||||
VBOX_IID_INITIALIZE(&parentiid);
|
||||
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 = gVBoxAPI.UIVirtualBox.OpenMedium(data->vboxObj,
|
||||
locationUtf16,
|
||||
DeviceType_HardDisk,
|
||||
AccessMode_ReadWrite,
|
||||
&medium);
|
||||
if (NS_FAILED(rc)) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR,
|
||||
_("Unable to open HardDisk, rc=%08x"),
|
||||
(unsigned)rc);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
rc = gVBoxAPI.UIMedium.GetId(medium, &parentiid);
|
||||
if (NS_FAILED(rc)) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR,
|
||||
_("Unable to get hardDisk Id, rc=%08x"),
|
||||
(unsigned)rc);
|
||||
goto cleanup;
|
||||
}
|
||||
gVBoxAPI.UIID.vboxIIDToUtf8(data, &parentiid, &parentUuid);
|
||||
vboxIIDUnalloc(&parentiid);
|
||||
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 = gVBoxAPI.UIVirtualBox.CreateHardDiskMedium(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);
|
||||
|
||||
PRUint32 tab[1];
|
||||
tab[0] = MediumVariant_Diff;
|
||||
gVBoxAPI.UIMedium.CreateDiffStorage(medium, newMedium, 1, tab, &progress);
|
||||
|
||||
gVBoxAPI.UIProgress.WaitForCompletion(progress, -1);
|
||||
gVBoxAPI.UIProgress.GetResultCode(progress, &resultCode);
|
||||
if (RC_FAILED(resultCode)) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR,
|
||||
_("Error while creating diff storage, rc=%08x"),
|
||||
resultCode.uResultCode);
|
||||
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 = gVBoxAPI.UIMedium.GetId(newMedium, &iid);
|
||||
if (NS_FAILED(rc)) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR,
|
||||
_("Unable to get medium uuid, rc=%08x"),
|
||||
(unsigned)rc);
|
||||
VIR_FREE(disk);
|
||||
goto cleanup;
|
||||
}
|
||||
gVBoxAPI.UIID.vboxIIDToUtf8(data, &iid, &uuid);
|
||||
disk->uuid = uuid;
|
||||
vboxIIDUnalloc(&iid);
|
||||
|
||||
if (VIR_STRDUP(disk->location, newLocationUtf8) < 0) {
|
||||
VIR_FREE(disk);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
rc = gVBoxAPI.UIMedium.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 = gVBoxAPI.UIMedium.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++) {
|
||||
const 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++) {
|
||||
const 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++) {
|
||||
const 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 = gVBoxAPI.UIMachine.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];
|
||||
PRUnichar *locationUtf16 = NULL;
|
||||
char *locationUtf8 = NULL;
|
||||
|
||||
if (!medium)
|
||||
continue;
|
||||
|
||||
rc = gVBoxAPI.UIMedium.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;
|
||||
resultCodeUnion resultCode;
|
||||
rc = gVBoxAPI.UIMedium.DeleteStorage(medium, &progress);
|
||||
if (NS_FAILED(rc)) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR,
|
||||
_("Unable to delete medium, rc=%08x"),
|
||||
(unsigned)rc);
|
||||
goto cleanup;
|
||||
}
|
||||
gVBoxAPI.UIProgress.WaitForCompletion(progress, -1);
|
||||
gVBoxAPI.UIProgress.GetResultCode(progress, &resultCode);
|
||||
if (RC_FAILED(resultCode)) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR,
|
||||
_("Error while closing medium, rc=%08x"),
|
||||
resultCode.uResultCode);
|
||||
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 = gVBoxAPI.UIMedium.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 = gVBoxAPI.UIVirtualBox.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 = gVBoxAPI.UIVirtualBox.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;
|
||||
}
|
||||
|
||||
int vboxDomainSnapshotDelete(virDomainSnapshotPtr snapshot,
|
||||
unsigned int flags)
|
||||
{
|
||||
virDomainPtr dom = snapshot->domain;
|
||||
VBOX_OBJECT_CHECK(dom->conn, int, -1);
|
||||
vboxIIDUnion domiid;
|
||||
IMachine *machine = NULL;
|
||||
ISnapshot *snap = NULL;
|
||||
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);
|
||||
|
||||
if (openSessionForMachine(data, dom->uuid, &domiid, &machine, false) < 0)
|
||||
goto cleanup;
|
||||
|
||||
snap = vboxDomainSnapshotGet(data, dom, machine, snapshot->name);
|
||||
if (!snap)
|
||||
goto cleanup;
|
||||
|
||||
rc = gVBoxAPI.UIMachine.GetState(machine, &state);
|
||||
if (NS_FAILED(rc)) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("could not get domain state"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* In case we just want to delete the metadata, we will edit the vbox file in order
|
||||
*to remove the node concerning the snapshot
|
||||
*/
|
||||
if (flags & VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY) {
|
||||
rc = gVBoxAPI.UArray.vboxArrayGet(&snapChildren, snap,
|
||||
gVBoxAPI.UArray.handleSnapshotGetChildren(snap));
|
||||
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 (gVBoxAPI.vboxSnapshotRedefine) {
|
||||
ret = vboxDomainSnapshotDeleteMetadataOnly(snapshot);
|
||||
}
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (gVBoxAPI.machineStateChecker.Online(state)) {
|
||||
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
|
||||
_("cannot delete snapshots of running domain"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
rc = gVBoxAPI.UISession.Open(data, &domiid, machine);
|
||||
if (NS_SUCCEEDED(rc))
|
||||
rc = gVBoxAPI.UISession.GetConsole(data->vboxSession, &console);
|
||||
if (NS_FAILED(rc)) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR,
|
||||
_("could not open VirtualBox session with domain %s"),
|
||||
dom->name);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (flags & VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN)
|
||||
ret = vboxDomainSnapshotDeleteTree(data, console, snap);
|
||||
else
|
||||
ret = vboxDomainSnapshotDeleteSingle(data, console, snap);
|
||||
|
||||
cleanup:
|
||||
VBOX_RELEASE(console);
|
||||
VBOX_RELEASE(snap);
|
||||
vboxIIDUnalloc(&domiid);
|
||||
gVBoxAPI.UISession.Close(data->vboxSession);
|
||||
return ret;
|
||||
}
|
||||
|
@ -265,6 +265,19 @@ enum MediumVariant
|
||||
MediumVariant_Diff = 0x20000
|
||||
};
|
||||
|
||||
# define VBOX_E_OBJECT_NOT_FOUND 0x80BB0001
|
||||
# define VBOX_E_INVALID_VM_STATE 0x80BB0002
|
||||
# define VBOX_E_VM_ERROR 0x80BB0003
|
||||
# define VBOX_E_FILE_ERROR 0x80BB0004
|
||||
# define VBOX_E_IPRT_ERROR 0x80BB0005
|
||||
# define VBOX_E_PDM_ERROR 0x80BB0006
|
||||
# define VBOX_E_INVALID_OBJECT_STATE 0x80BB0007
|
||||
# define VBOX_E_HOST_ERROR 0x80BB0008
|
||||
# define VBOX_E_NOT_SUPPORTED 0x80BB0009
|
||||
# define VBOX_E_XML_ERROR 0x80BB000A
|
||||
# define VBOX_E_INVALID_SESSION_STATE 0x80BB000B
|
||||
# define VBOX_E_OBJECT_IN_USE 0x80BB000C
|
||||
|
||||
/* Simplied definitions in vbox_CAPI_*.h */
|
||||
|
||||
typedef void const *PCVBOXXPCOM;
|
||||
|
@ -1383,146 +1383,6 @@ _vboxAttachDrivesOld(virDomainDefPtr def ATTRIBUTE_UNUSED,
|
||||
|
||||
#endif /* VBOX_API_VERSION >= 4000000 */
|
||||
|
||||
static int
|
||||
vboxDomainSnapshotGetAll(virDomainPtr dom,
|
||||
IMachine *machine,
|
||||
ISnapshot ***snapshots)
|
||||
{
|
||||
vboxIID empty = VBOX_IID_INITIALIZER;
|
||||
ISnapshot **list = NULL;
|
||||
PRUint32 count;
|
||||
nsresult rc;
|
||||
unsigned int next;
|
||||
unsigned int top;
|
||||
|
||||
rc = machine->vtbl->GetSnapshotCount(machine, &count);
|
||||
if (NS_FAILED(rc)) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR,
|
||||
_("could not get snapshot count for domain %s"),
|
||||
dom->name);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (count == 0)
|
||||
goto out;
|
||||
|
||||
if (VIR_ALLOC_N(list, count) < 0)
|
||||
goto error;
|
||||
|
||||
#if VBOX_API_VERSION < 4000000
|
||||
rc = machine->vtbl->GetSnapshot(machine, empty.value, list);
|
||||
#else /* VBOX_API_VERSION >= 4000000 */
|
||||
rc = machine->vtbl->FindSnapshot(machine, empty.value, list);
|
||||
#endif /* VBOX_API_VERSION >= 4000000 */
|
||||
if (NS_FAILED(rc) || !list[0]) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR,
|
||||
_("could not get root snapshot for domain %s"),
|
||||
dom->name);
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* BFS walk through snapshot tree */
|
||||
top = 1;
|
||||
for (next = 0; next < count; next++) {
|
||||
vboxArray children = VBOX_ARRAY_INITIALIZER;
|
||||
size_t i;
|
||||
|
||||
if (!list[next]) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR,
|
||||
_("unexpected number of snapshots < %u"), count);
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = vboxArrayGet(&children, list[next],
|
||||
list[next]->vtbl->GetChildren);
|
||||
if (NS_FAILED(rc)) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR,
|
||||
"%s", _("could not get children snapshots"));
|
||||
goto error;
|
||||
}
|
||||
for (i = 0; i < children.count; i++) {
|
||||
ISnapshot *child = children.items[i];
|
||||
if (!child)
|
||||
continue;
|
||||
if (top == count) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR,
|
||||
_("unexpected number of snapshots > %u"), count);
|
||||
vboxArrayRelease(&children);
|
||||
goto error;
|
||||
}
|
||||
VBOX_ADDREF(child);
|
||||
list[top++] = child;
|
||||
}
|
||||
vboxArrayRelease(&children);
|
||||
}
|
||||
|
||||
out:
|
||||
*snapshots = list;
|
||||
return count;
|
||||
|
||||
error:
|
||||
if (list) {
|
||||
for (next = 0; next < count; next++)
|
||||
VBOX_RELEASE(list[next]);
|
||||
}
|
||||
VIR_FREE(list);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static ISnapshot *
|
||||
vboxDomainSnapshotGet(vboxGlobalData *data,
|
||||
virDomainPtr dom,
|
||||
IMachine *machine,
|
||||
const char *name)
|
||||
{
|
||||
ISnapshot **snapshots = NULL;
|
||||
ISnapshot *snapshot = NULL;
|
||||
nsresult rc;
|
||||
int count = 0;
|
||||
size_t i;
|
||||
|
||||
if ((count = vboxDomainSnapshotGetAll(dom, machine, &snapshots)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
PRUnichar *nameUtf16;
|
||||
char *nameUtf8;
|
||||
|
||||
rc = snapshots[i]->vtbl->GetName(snapshots[i], &nameUtf16);
|
||||
if (NS_FAILED(rc) || !nameUtf16) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR,
|
||||
"%s", _("could not get snapshot name"));
|
||||
goto cleanup;
|
||||
}
|
||||
VBOX_UTF16_TO_UTF8(nameUtf16, &nameUtf8);
|
||||
VBOX_UTF16_FREE(nameUtf16);
|
||||
if (STREQ(name, nameUtf8))
|
||||
snapshot = snapshots[i];
|
||||
VBOX_UTF8_FREE(nameUtf8);
|
||||
|
||||
if (snapshot)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!snapshot) {
|
||||
virReportError(VIR_ERR_OPERATION_INVALID,
|
||||
_("domain %s has no snapshots with name %s"),
|
||||
dom->name, name);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (count > 0) {
|
||||
for (i = 0; i < count; i++) {
|
||||
if (snapshots[i] != snapshot)
|
||||
VBOX_RELEASE(snapshots[i]);
|
||||
}
|
||||
}
|
||||
VIR_FREE(snapshots);
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
#if VBOX_API_VERSION < 3001000
|
||||
static int
|
||||
_vboxDomainSnapshotRestore(virDomainPtr dom,
|
||||
@ -1630,633 +1490,6 @@ _vboxDomainSnapshotRestore(virDomainPtr dom,
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
vboxDomainSnapshotDeleteSingle(vboxGlobalData *data,
|
||||
IConsole *console,
|
||||
ISnapshot *snapshot)
|
||||
{
|
||||
IProgress *progress = NULL;
|
||||
vboxIID iid = VBOX_IID_INITIALIZER;
|
||||
int ret = -1;
|
||||
nsresult rc;
|
||||
#if VBOX_API_VERSION == 2002000
|
||||
nsresult result;
|
||||
#else
|
||||
PRInt32 result;
|
||||
#endif
|
||||
|
||||
rc = snapshot->vtbl->GetId(snapshot, &iid.value);
|
||||
if (NS_FAILED(rc)) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("could not get snapshot UUID"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
#if VBOX_API_VERSION < 3001000
|
||||
rc = console->vtbl->DiscardSnapshot(console, iid.value, &progress);
|
||||
#else
|
||||
rc = console->vtbl->DeleteSnapshot(console, iid.value, &progress);
|
||||
#endif
|
||||
if (NS_FAILED(rc) || !progress) {
|
||||
if (rc == VBOX_E_INVALID_VM_STATE) {
|
||||
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
|
||||
_("cannot delete domain snapshot for running domain"));
|
||||
} else {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("could not delete snapshot"));
|
||||
}
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
progress->vtbl->WaitForCompletion(progress, -1);
|
||||
progress->vtbl->GetResultCode(progress, &result);
|
||||
if (NS_FAILED(result)) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("could not delete snapshot"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
cleanup:
|
||||
VBOX_RELEASE(progress);
|
||||
vboxIIDUnalloc(&iid);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
vboxDomainSnapshotDeleteTree(vboxGlobalData *data,
|
||||
IConsole *console,
|
||||
ISnapshot *snapshot)
|
||||
{
|
||||
vboxArray children = VBOX_ARRAY_INITIALIZER;
|
||||
int ret = -1;
|
||||
nsresult rc;
|
||||
size_t i;
|
||||
|
||||
rc = vboxArrayGet(&children, snapshot, snapshot->vtbl->GetChildren);
|
||||
if (NS_FAILED(rc)) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("could not get children snapshots"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
for (i = 0; i < children.count; i++) {
|
||||
if (vboxDomainSnapshotDeleteTree(data, console, children.items[i]))
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = vboxDomainSnapshotDeleteSingle(data, console, snapshot);
|
||||
|
||||
cleanup:
|
||||
vboxArrayRelease(&children);
|
||||
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);
|
||||
VIR_FREE(disk);
|
||||
goto cleanup;
|
||||
}
|
||||
VBOX_UTF16_TO_UTF8(uuidUtf16, &uuid);
|
||||
disk->uuid = uuid;
|
||||
VBOX_UTF16_FREE(uuidUtf16);
|
||||
|
||||
if (VIR_STRDUP(disk->location, newLocationUtf8) < 0) {
|
||||
VIR_FREE(disk);
|
||||
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++) {
|
||||
const 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++) {
|
||||
const 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++) {
|
||||
const 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,
|
||||
unsigned int flags)
|
||||
{
|
||||
virDomainPtr dom = snapshot->domain;
|
||||
VBOX_OBJECT_CHECK(dom->conn, int, -1);
|
||||
vboxIID domiid = VBOX_IID_INITIALIZER;
|
||||
IMachine *machine = NULL;
|
||||
ISnapshot *snap = NULL;
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
snap = vboxDomainSnapshotGet(data, dom, machine, snapshot->name);
|
||||
if (!snap)
|
||||
goto cleanup;
|
||||
|
||||
rc = machine->vtbl->GetState(machine, &state);
|
||||
if (NS_FAILED(rc)) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("could not get domain state"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* In case we just want to delete the metadata, we will edit the vbox file in order
|
||||
*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"));
|
||||
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;
|
||||
}
|
||||
|
||||
if (state >= MachineState_FirstOnline
|
||||
&& state <= MachineState_LastOnline) {
|
||||
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
|
||||
_("cannot delete snapshots of running domain"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
rc = VBOX_SESSION_OPEN(domiid.value, machine);
|
||||
if (NS_SUCCEEDED(rc))
|
||||
rc = data->vboxSession->vtbl->GetConsole(data->vboxSession, &console);
|
||||
if (NS_FAILED(rc)) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR,
|
||||
_("could not open VirtualBox session with domain %s"),
|
||||
dom->name);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (flags & VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN)
|
||||
ret = vboxDomainSnapshotDeleteTree(data, console, snap);
|
||||
else
|
||||
ret = vboxDomainSnapshotDeleteSingle(data, console, snap);
|
||||
|
||||
cleanup:
|
||||
VBOX_RELEASE(console);
|
||||
VBOX_RELEASE(snap);
|
||||
vboxIIDUnalloc(&domiid);
|
||||
VBOX_SESSION_CLOSE();
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if VBOX_API_VERSION <= 2002000 || VBOX_API_VERSION >= 4000000
|
||||
/* No Callback support for VirtualBox 2.2.* series */
|
||||
/* No Callback support for VirtualBox 4.* series */
|
||||
@ -6231,6 +5464,16 @@ _consoleTakeSnapshot(IConsole *console, PRUnichar *name,
|
||||
return console->vtbl->TakeSnapshot(console, name, description, progress);
|
||||
}
|
||||
|
||||
static nsresult
|
||||
_consoleDeleteSnapshot(IConsole *console, vboxIIDUnion *iidu, IProgress **progress)
|
||||
{
|
||||
#if VBOX_API_VERSION < 3001000
|
||||
return console->vtbl->DiscardSnapshot(console, IID_MEMBER(value), progress);
|
||||
#else /* VBOX_API_VERSION >= 3001000 */
|
||||
return console->vtbl->DeleteSnapshot(console, IID_MEMBER(value), progress);
|
||||
#endif /* VBOX_API_VERSION >= 3001000 */
|
||||
}
|
||||
|
||||
static nsresult
|
||||
_progressWaitForCompletion(IProgress *progress, PRInt32 timeout)
|
||||
{
|
||||
@ -7243,6 +6486,7 @@ static vboxUniformedIConsole _UIConsole = {
|
||||
.PowerDown = _consolePowerDown,
|
||||
.Reset = _consoleReset,
|
||||
.TakeSnapshot = _consoleTakeSnapshot,
|
||||
.DeleteSnapshot = _consoleDeleteSnapshot,
|
||||
};
|
||||
|
||||
static vboxUniformedIProgress _UIProgress = {
|
||||
|
@ -272,6 +272,7 @@ typedef struct {
|
||||
nsresult (*Reset)(IConsole *console);
|
||||
nsresult (*TakeSnapshot)(IConsole *console, PRUnichar *name,
|
||||
PRUnichar *description, IProgress **progress);
|
||||
nsresult (*DeleteSnapshot)(IConsole *console, vboxIIDUnion *iidu, IProgress **progress);
|
||||
} vboxUniformedIConsole;
|
||||
|
||||
/* Functions for IProgress */
|
||||
@ -596,6 +597,9 @@ int vboxDomainSnapshotHasMetadata(virDomainSnapshotPtr snapshot,
|
||||
unsigned int flags);
|
||||
int vboxDomainRevertToSnapshot(virDomainSnapshotPtr snapshot,
|
||||
unsigned int flags);
|
||||
int vboxDomainSnapshotDelete(virDomainSnapshotPtr snapshot,
|
||||
unsigned int flags);
|
||||
|
||||
|
||||
/* Version specified functions for installing uniformed API */
|
||||
void vbox22InstallUniformedAPI(vboxUniformedAPI *pVBoxAPI);
|
||||
|
Loading…
x
Reference in New Issue
Block a user