MachineImplCloneVM.cpp revision 3f2ba43dc4534d2374c8758a4b727d7e59572c1c
/* $Id$ */
/** @file
* Implementation of MachineCloneVM
*/
/*
* Copyright (C) 2011 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
#include "MachineImplCloneVM.h"
#include "VirtualBoxImpl.h"
#include "MediumImpl.h"
// typedefs
/////////////////////////////////////////////////////////////////////////////
typedef struct
{
typedef struct
{
bool fCreateDiffs;
typedef struct
{
// The private class
/////////////////////////////////////////////////////////////////////////////
struct MachineCloneVMPrivate
{
MachineCloneVMPrivate(MachineCloneVM *a_q, ComObjPtr<Machine> &a_pSrcMachine, ComObjPtr<Machine> &a_pTrgMachine, CloneMode_T a_mode, bool a_fFullClone)
, p(a_pSrcMachine)
{}
/* Thread management */
int startWorker()
{
return RTThreadCreate(NULL,
static_cast<void*>(this),
0,
0,
"MachineClone");
}
{
return VINF_SUCCESS;
}
/* Private helper methods */
HRESULT cloneCreateMachineList(const ComPtr<ISnapshot> &pSnapshot, RTCList< ComObjPtr<Machine> > &machineList) const;
settings::Snapshot cloneFindSnapshot(settings::MachineConfigFile *pMCF, const settings::SnapshotsList &snl, const Guid &id) const;
void cloneUpdateStorageLists(settings::StorageControllersList &sc, const Bstr &bstrOldId, const Bstr &bstrNewId) const;
void cloneUpdateSnapshotStorageLists(settings::SnapshotsList &sl, const Bstr &bstrOldId, const Bstr &bstrNewId) const;
void cloneUpdateStateFile(settings::SnapshotsList &snl, const Guid &id, const Utf8Str &strFile) const;
/* Private q and parent pointer */
/* Private helper members */
bool fLinkDisks;
};
HRESULT MachineCloneVMPrivate::cloneCreateMachineList(const ComPtr<ISnapshot> &pSnapshot, RTCList< ComObjPtr<Machine> > &machineList) const
{
{
}
return rc;
}
settings::Snapshot MachineCloneVMPrivate::cloneFindSnapshot(settings::MachineConfigFile *pMCF, const settings::SnapshotsList &snl, const Guid &id) const
{
{
return *it;
}
}
void MachineCloneVMPrivate::cloneUpdateStorageLists(settings::StorageControllersList &sc, const Bstr &bstrOldId, const Bstr &bstrNewId) const
{
++it3)
{
++it4)
{
{
}
}
}
}
void MachineCloneVMPrivate::cloneUpdateSnapshotStorageLists(settings::SnapshotsList &sl, const Bstr &bstrOldId, const Bstr &bstrNewId) const
{
++it)
{
}
}
void MachineCloneVMPrivate::cloneUpdateStateFile(settings::SnapshotsList &snl, const Guid &id, const Utf8Str &strFile) const
{
{
}
}
/* static */
{
/* If canceled by the user tell it to the copy operation. */
if (fCanceled) return VERR_CANCELLED;
/* Set the new process. */
return VINF_SUCCESS;
}
// The public class
/////////////////////////////////////////////////////////////////////////////
MachineCloneVM::MachineCloneVM(ComObjPtr<Machine> pSrcMachine, ComObjPtr<Machine> pTrgMachine, CloneMode_T mode, bool fFullClone)
{
}
{
delete d_ptr;
}
{
try
{
/* Lock the target machine early (so nobody mess around with it in the meantime). */
if (d->pSrcMachine->isSnapshotMachine())
/* Add the current machine and all snapshot machines below this machine
* in a list for further processing. */
/* Include current state? */
if ( d->mode == CloneMode_MachineState)
// || d->mode == CloneMode_AllStates)
/* Should be done a depth copy with all child snapshots? */
if ( d->mode == CloneMode_MachineAndChildStates
|| d->mode == CloneMode_AllStates)
{
ULONG cSnapshots = 0;
if (cSnapshots > 0)
{
if ( d->mode == CloneMode_MachineAndChildStates
&& !d->snapshotId.isEmpty())
}
}
/* Go over every machine and walk over every attachment this machine has. */
{
/* If this is the Snapshot Machine we want to clone, we need to
* create a new diff file for the new "current state". */
bool fCreateDiffs = false;
if (machine == d->pOldMachineState)
fCreateDiffs = true;
/* Add all attachments (and there parents) of the different
* machines to a worker list. */
{
/* Only harddisk's are of interest. */
if (type != DeviceType_HardDisk)
continue;
/* Valid medium attached? */
if (pSrcMedium.isNull())
continue;
/* Build up a child->parent list of this attachment. (Note: we are
* not interested of any child's not attached to this VM. So this
while(!pSrcMedium.isNull())
{
/* Refresh the state so that the file size get read. */
/* Save the current medium, for later cloning. */
/* Calculate progress data */
++uCount;
uTotalWeight += lSize;
/* Query next parent. */
};
}
if (!bstrSrcSaveStatePath.isEmpty())
{
if (RT_FAILURE(vrc))
throw p->setError(VBOX_E_IPRT_ERROR, p->tr("Could not query file size of '%s' (%Rrc)"), sst.strSaveStateFile.c_str(), vrc);
++uCount;
}
}
true /* fCancellable */,
1);
int vrc = d->startWorker();
if (RT_FAILURE(vrc))
}
{
}
return rc;
}
{
AutoCaller autoCaller(p);
/*
* Todo:
* - Regardless where the old media comes from (e.g. snapshots folder) it
* goes to the new main VM folder. Maybe we like to be a little bit
* smarter here.
* - Snapshot diffs (can) have the uuid as name. After cloning this isn't
* right anymore. Is it worth to change to the new uuid? Or should the
* cloned disks called exactly as the original one or should all new disks
* get a new name with the new VM name in it.
*/
/* Where should all the media go? */
try
{
/* Copy all the configuration from this machine to an empty
* configuration dataset. */
/* Reset media registry. */
* with the stuff from the snapshot. */
if (!d->snapshotId.isEmpty())
if (d->mode == CloneMode_MachineState)
{
{
}
/* Remove any hint on snapshots. */
}else
if ( d->mode == CloneMode_MachineAndChildStates
{
/* Copy the snapshot data to the current machine. */
/* The snapshot will be the root one. */
}
/* When the current snapshot folder is absolute we reset it to the
* default relative folder. */
/* Force writing of setting file. */
pTrgMCF->fCurrentStateModified = true;
/* Set the new name. */
/* The absolute name of the snapshot folder. */
Utf8Str strTrgSnapshotFolder = Utf8StrFmt("%s%c%s%c", strTrgMachineFolder.c_str(), RTPATH_DELIMITER, pTrgMCF->machineUserData.strSnapshotFolder.c_str(), RTPATH_DELIMITER);
/* We need to create a map with the already created medias. This is
* necessary, cause different snapshots could have the same
* cloned a second time, but simply used. */
{
{
rc = d->pProgress->SetNextOperation(BstrFmt(p->tr("Cloning Disk '%ls' ..."), bstrSrcName.raw()).raw(), mt.uSize);
/* Is a clone already there? */
else
{
/* Is the source file based? */
{
/* Yes, just use the source format. Otherwise the defaults
* will be used. */
}
/* Start creating the clone. */
Utf8Str strFile = Utf8StrFmt("%s%c%lS", strTrgMachineFolder.c_str(), RTPATH_DELIMITER, bstrSrcName.raw());
NULL /* llRegistriesThatNeedSaving */);
/* Do the disk cloning. */
progress2.asOutParam());
/* Wait until the asynchrony process has finished. */
/* Check the result of the asynchrony process. */
{
/* If the thread of the progress object has an error, then
* retrieve the error info from there, or it'll be lost. */
}
/* Remember created medias. */
/* This medium becomes the parent of the next medium in the
* chain. */
}
}
/* Create diffs for the last image chain. */
if (mtc.fCreateDiffs)
{
diff.createObject();
NULL); // pllRegistriesThatNeedSaving
true /* fMediumLockWrite */,
NULL /* aProgress */,
true /* aWait */,
NULL); // pllRegistriesThatNeedSaving
delete pMediumLockList;
pNewParent = diff;
}
/* We have to patch the configuration, so it contains the new
* medium uuid instead of the old one. */
}
/* Clone all save state files. */
{
const Utf8Str &strTrgSaveState = Utf8StrFmt("%s%s", strTrgSnapshotFolder.c_str(), RTPathFilename(sst.strSaveStateFile.c_str()));
/* Move to next sub-operation. */
rc = d->pProgress->SetNextOperation(BstrFmt(p->tr("Copy save state file '%s' ..."), RTPathFilename(sst.strSaveStateFile.c_str())).raw(), sst.cbSize);
/* Copy the file only if it was not copied already. */
{
int vrc = RTFileCopyEx(sst.strSaveStateFile.c_str(), strTrgSaveState.c_str(), 0, MachineCloneVMPrivate::cloneCopyStateFileProgress, &d->pProgress);
if (RT_FAILURE(vrc))
throw p->setError(VBOX_E_IPRT_ERROR,
p->tr("Could not copy state file '%s' to '%s' (%Rrc)"), sst.strSaveStateFile.c_str(), strTrgSaveState.c_str(), vrc);
}
/* Update the path in the configuration either for the current
* machine state or the snapshots. */
else
}
if (false)
// if (!d->pOldMachineState.isNull())
{
{
/* Only harddisk's are of interest. */
if (type != DeviceType_HardDisk)
continue;
/* Valid medium attached? */
if (pSrcMedium.isNull())
continue;
// ComObjPtr<Medium> pMedium = static_cast<Medium*>((IMedium*)pSrcMedium);
// ComObjPtr<Medium> diff;
// diff.createObject();
// store this diff in the same registry as the parent
// Guid uuidRegistryParent;
// if (!medium->getFirstRegistryMachineId(uuidRegistryParent))
// {
// parent image has no registry: this can happen if we're attaching a new immutable
// image that has not yet been attached (medium then points to the base and we're
// creating the diff image for the immutable, and the parent is not yet registered);
// put the parent in the machine registry then
// addMediumToRegistry(medium, llRegistriesThatNeedSaving, &uuidRegistryParent);
// }
// rc = diff->init(mParent,
// pMedium->getPreferredDiffFormat(),
// strFullSnapshotFolder.append(RTPATH_SLASH_STR),
// uuidRegistryParent,
// pllRegistriesThatNeedSaving);
// if (FAILED(rc)) throw rc;
//
// rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
// pMediumLockList,
// NULL /* aProgress */,
// true /* aWait */,
// pllRegistriesThatNeedSaving);
}
}
{
rc = d->pProgress->SetNextOperation(BstrFmt(p->tr("Create Machine Clone '%s' ..."), pTrgMCF->machineUserData.strName.c_str()).raw(), 1);
/* After modifying the new machine config, we can copy the stuff
* over to the new machine. The machine have to be mutable for
* this. */
}
/* The medias are created before the machine was there. We have to make
* sure the new medias know of there new parent or we get in trouble
* when the media registry is saved for this VM, especially in case of
* difference image chain's. See VirtualBox::saveMediaRegistry.*/
// for (size_t i = 0; i < newBaseMedias.size(); ++i)
// {
// rc = newBaseMedias.at(i)->addRegistry(d->pTrgMachine->mData->mUuid, true /* fRecursive */);
// if (FAILED(rc)) throw rc;
// }
/* Now save the new configuration to disk. */
}
{
}
catch (...)
{
}
/* Cleanup on failure (CANCEL also) */
{
int vrc = VINF_SUCCESS;
/* Delete all created files. */
{
if (RT_FAILURE(vrc))
rc = p->setError(VBOX_E_IPRT_ERROR, p->tr("Could not delete file '%s' (%Rrc)"), newFiles.at(i).c_str(), vrc);
}
/* Delete all already created medias. (Reverse, cause there could be
* parent->child relations.) */
{
/* Close the medium. If this succeed, delete it finally from the
* disk. */
if (fFile)
{
if (RT_FAILURE(vrc))
rc = p->setError(VBOX_E_IPRT_ERROR, p->tr("Could not delete file '%s' (%Rrc)"), strLoc.c_str(), vrc);
}
}
/* Delete the machine folder when not empty. */
}
return rc;
}
void MachineCloneVM::destroy()
{
delete this;
}