ApplianceImpl.cpp revision 6a74d2edb239c0daeb51ccf267b4f66b7add5335
/* $Id$ */
/** @file
*
* IAppliance and IVirtualSystem COM class implementations.
*/
/*
* Copyright (C) 2008-2013 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 <map>
#include "ApplianceImpl.h"
#include "VFSExplorerImpl.h"
#include "VirtualBoxImpl.h"
#include "GuestOSTypeImpl.h"
#include "Global.h"
#include "ProgressImpl.h"
#include "MachineImpl.h"
#include "SystemPropertiesImpl.h"
#include "AutoCaller.h"
#include "Logging.h"
#include "ApplianceImplPrivate.h"
using namespace std;
////////////////////////////////////////////////////////////////////////////////
//
// Appliance constructor / destructor
//
// ////////////////////////////////////////////////////////////////////////////////
{
return BaseFinalConstruct();
}
void VirtualSystemDescription::FinalRelease()
{
uninit();
}
: mVirtualBox(NULL)
{
}
{
}
{
return BaseFinalConstruct();
}
void Appliance::FinalRelease()
{
uninit();
}
////////////////////////////////////////////////////////////////////////////////
//
// Internal helpers
//
////////////////////////////////////////////////////////////////////////////////
static const char* const strISOURI = "http://www.ecma-international.org/publications/standards/Ecma-119.htm";
static const char* const strVMDKStreamURI = "http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized";
static const char* const strVMDKSparseURI = "http://www.vmware.com/specifications/vmdk.html#sparse";
static const char* const strVMDKCompressedURI = "http://www.vmware.com/specifications/vmdk.html#compressed";
static const char* const strVMDKCompressedURI2 = "http://www.vmware.com/interfaces/specifications/vmdk.html#compressed";
static const char* const strVHDURI = "http://go.microsoft.com/fwlink/?LinkId=137171";
static const char* const applianceIOTarName = "Appliance::IOTar";
static const char* const applianceIOShaName = "Appliance::IOSha";
static const char* const applianceIOFileName = "Appliance::IOFile";
static const struct
{
}
g_osTypes[] =
{
// Linuxes
// types that we have support for but CIM doesn't
// types added with CIM 2.25.0 follow:
// { ovf::CIMOSType_CIMOS_VMwareESXi = 104, // we can't run ESX in a VM
// there are no CIM types for these, so these turn to "other" on export:
// VBOXOSTYPE_OpenBSD
// VBOXOSTYPE_OpenBSD_x64
// VBOXOSTYPE_NetBSD
// VBOXOSTYPE_NetBSD_x64
};
/* Pattern structure for matching the OS type description field */
struct osTypePattern
{
const char *pcszPattern;
};
/* These are the 32-Bit ones. They are sorted by priority. */
static const osTypePattern g_osTypesPattern[] =
{
{"Windows NT", VBOXOSTYPE_WinNT4},
{"Windows XP", VBOXOSTYPE_WinXP},
{"Windows 2000", VBOXOSTYPE_Win2k},
{"Windows 2003", VBOXOSTYPE_Win2k3},
{"Windows Vista", VBOXOSTYPE_WinVista},
{"Windows 2008", VBOXOSTYPE_Win2k8},
{"SUSE", VBOXOSTYPE_OpenSUSE},
{"Novell", VBOXOSTYPE_OpenSUSE},
{"Red Hat", VBOXOSTYPE_RedHat},
{"Mandriva", VBOXOSTYPE_Mandriva},
{"Ubuntu", VBOXOSTYPE_Ubuntu},
{"Debian", VBOXOSTYPE_Debian},
{"QNX", VBOXOSTYPE_QNX},
{"Linux 2.4", VBOXOSTYPE_Linux24},
{"Linux 2.6", VBOXOSTYPE_Linux26},
{"Linux", VBOXOSTYPE_Linux},
{"OpenSolaris", VBOXOSTYPE_OpenSolaris},
{"Solaris", VBOXOSTYPE_OpenSolaris},
{"FreeBSD", VBOXOSTYPE_FreeBSD},
{"NetBSD", VBOXOSTYPE_NetBSD},
{"Windows 95", VBOXOSTYPE_Win95},
{"Windows 98", VBOXOSTYPE_Win98},
{"Windows Me", VBOXOSTYPE_WinMe},
{"Windows 3.", VBOXOSTYPE_Win31},
{"DOS", VBOXOSTYPE_DOS},
{"OS2", VBOXOSTYPE_OS2}
};
/* These are the 64-Bit ones. They are sorted by priority. */
static const osTypePattern g_osTypesPattern64[] =
{
{"Windows XP", VBOXOSTYPE_WinXP_x64},
{"Windows 2003", VBOXOSTYPE_Win2k3_x64},
{"Windows Vista", VBOXOSTYPE_WinVista_x64},
{"Windows 2008", VBOXOSTYPE_Win2k8_x64},
{"SUSE", VBOXOSTYPE_OpenSUSE_x64},
{"Novell", VBOXOSTYPE_OpenSUSE_x64},
{"Red Hat", VBOXOSTYPE_RedHat_x64},
{"Mandriva", VBOXOSTYPE_Mandriva_x64},
{"Ubuntu", VBOXOSTYPE_Ubuntu_x64},
{"Debian", VBOXOSTYPE_Debian_x64},
{"Linux 2.4", VBOXOSTYPE_Linux24_x64},
{"Linux 2.6", VBOXOSTYPE_Linux26_x64},
{"Linux", VBOXOSTYPE_Linux26_x64},
{"OpenSolaris", VBOXOSTYPE_OpenSolaris_x64},
{"Solaris", VBOXOSTYPE_OpenSolaris_x64},
{"FreeBSD", VBOXOSTYPE_FreeBSD_x64},
};
/**
* Private helper func that suggests a VirtualBox guest OS type
* for the given OVF operating system type.
* @param osTypeVBox
* @param c
* @param cStr
*/
{
if (c == ovf::CIMOSType_CIMOS_Other)
{
{
return;
}
}
else if (c == ovf::CIMOSType_CIMOS_Other_64)
{
{
return;
}
}
{
{
return;
}
}
if (c == ovf::CIMOSType_CIMOS_Other_64)
else
}
/**
* Private helper func that suggests a VirtualBox guest OS type
* for the given OVF operating system type.
* @returns CIM OS type.
* @param pcszVBox Our guest OS type identifier string.
* @param fLongMode Whether long mode is enabled and a 64-bit CIM type is
* preferred even if the VBox guest type isn't 64-bit.
*/
{
{
{
{
size_t j = i;
while (++j < RT_ELEMENTS(g_osTypes))
j = i;
while (--j > 0)
/* Not all OSes have 64-bit versions, so just return the 32-bit variant. */
}
}
}
}
{
switch (type)
{
}
return strType;
}
////////////////////////////////////////////////////////////////////////////////
//
// IVirtualBox public methods
//
////////////////////////////////////////////////////////////////////////////////
// This code is here so we won't have to include the appliance headers in the
// IVirtualBox implementation.
/**
* Implementation for IVirtualBox::createAppliance.
*
* @param anAppliance IAppliance object created if S_OK is returned.
* @return S_OK or error.
*/
{
return rc;
}
/**
* Appliance COM initializer.
* @param
* @return
*/
{
/* Enclose the state transition NotReady->InInit->Ready */
AutoInitSpan autoInitSpan(this);
/* Weak reference to a VirtualBox object */
// initialize data
m = new Data;
/* Confirm a successful initialization */
return rc;
}
/**
* Appliance COM uninitializer.
* @return
*/
{
/* Enclose the state transition Ready->InUninit->NotReady */
AutoUninitSpan autoUninitSpan(this);
if (autoUninitSpan.uninitDone())
return;
delete m;
m = NULL;
}
////////////////////////////////////////////////////////////////////////////////
//
// IAppliance public methods
//
////////////////////////////////////////////////////////////////////////////////
/**
* Public method implementation.
* @param
* @return
*/
{
if (!i_isApplianceIdle())
return E_ACCESSDENIED;
return S_OK;
}
/**
* Public method implementation.
* @param
* @return
*/
{
if (!i_isApplianceIdle())
return E_ACCESSDENIED;
if (m->pReader) // OVFReader instantiated?
{
size_t i = 0;
++it, ++i)
{
// create a string representing this disk
"%s\t"
"%RI64\t"
"%RI64\t"
"%s\t"
"%s\t"
"%RI64\t"
"%RI64\t"
"%s",
d.iCapacity,
d.iSize,
d.iChunkSize,
d.strCompression.c_str());
}
}
return S_OK;
}
/**
* Public method implementation.
* @param
* @return
*/
HRESULT Appliance::getVirtualSystemDescriptions(std::vector<ComPtr<IVirtualSystemDescription> > &aVirtualSystemDescriptions)
{
if (!i_isApplianceIdle())
return E_ACCESSDENIED;
size_t i = 0;
for (std::list< ComObjPtr<VirtualSystemDescription> >::iterator it = vsds.begin(); it != vsds.end(); ++it, ++i)
{
}
return S_OK;
}
/**
* Public method implementation.
* @param aDisks
* @return
*/
{
if (!i_isApplianceIdle())
return E_ACCESSDENIED;
size_t i = 0;
++it, ++i)
{
}
return S_OK;
}
{
try
{
/* Check which kind of export the user has requested */
/* Create the explorer object */
rc = explorer->init(li.storageType, li.strPath, li.strHostname, li.strUsername, li.strPassword, mVirtualBox);
}
{
}
/* Return explorer to the caller */
return rc;
}
/**
* Public method implementation.
* @return
*/
{
size_t i = 0;
++it, ++i)
{
}
return S_OK;
}
////////////////////////////////////////////////////////////////////////////////
//
// Appliance private methods
//
////////////////////////////////////////////////////////////////////////////////
//
{
if (!supportedStandardsURI.empty())
return rc;
/* Get the system properties. */
{
return setError(E_FAIL, tr("Can't find appropriate medium format for ISO type of a virtual disk."));
}
{
return setError(E_FAIL, tr("Can't find appropriate medium format for VMDK type of a virtual disk."));
}
{
return setError(E_FAIL, tr("Can't find appropriate medium format for VHD type of a virtual disk."));
}
return rc;
}
{
{
}
return type;
}
{
{
++cit;
}
return uri;
}
{
if (!applianceIONameMap.empty())
return rc;
return rc;
}
{
{
}
return name;
}
/**
* Returns a medium format object corresponding to the given
* disk image or null if no such format.
*
* @param di Disk Image
* @param mf Medium Format
*
* @return ComObjPtr<MediumFormat>
*/
HRESULT Appliance::i_findMediumFormatFromDiskImage(const ovf::DiskImage &di, ComObjPtr<MediumFormat>& mf)
{
/* Get the system properties. */
/* We need a proper source format description */
/* Which format to use? */
/*
* fallback, if we can't determine virtual disk format using URI from the attribute ovf:format
* in the corresponding section <Disk> in the OVF file.
*/
if (strSrcFormat.isEmpty())
{
/* Figure out from extension which format the image of disk has. */
{
if (pszExt)
pszExt++;
}
}
else
{
tr("Internal inconsistency looking up medium format for the disk image '%s'"),
}
return rc;
}
/**
* Returns true if the appliance is in "idle" state. This should always be the
* case unless an import or export is currently in progress. Similar to machine
* states, this permits the Appliance implementation code to let go of the
* Appliance object lock while a time-consuming disk conversion is in progress
* without exposing the appliance to conflicting calls.
*
* This sets an error on "this" (the appliance) and returns false if the appliance
* is busy. The caller should then return E_ACCESSDENIED.
*
* Must be called from under the object lock!
*
* @return
*/
bool Appliance::i_isApplianceIdle()
{
else
return true;
return false;
}
{
int i = 1;
/** @todo: Maybe too cost-intensive; try to find a lighter way */
{
++i;
}
return S_OK;
}
{
int i = 1;
/* Check if the file exists or if a file with this path is registered
* already */
/** @todo: Maybe too cost-intensive; try to find a lighter way */
while ( RTPathExists(tmpName)
|| mVirtualBox->OpenMedium(Bstr(tmpName).raw(), DeviceType_HardDisk, AccessMode_ReadWrite, FALSE /* fForceNewUuid */, &harddisk) != VBOX_E_OBJECT_NOT_FOUND
)
{
++i;
}
return S_OK;
}
/**
* Called from Appliance::importImpl() and Appliance::writeImpl() to set up a
* progress object with the proper weights and maximum progress values.
*
* @param pProgress
* @param bstrDescription
* @param mode
* @return
*/
const Bstr &bstrDescription,
{
/* Create the progress object */
// compute the disks weight (this sets ulTotalDisksMB and cDisks in the instance data)
m->ulWeightForManifestOperation = 0;
+ m->cDisks; // plus one per disk
if (m->ulTotalDisksMB)
{
m->ulWeightForXmlOperation = (ULONG)((double)m->ulTotalDisksMB * 1 / 100); // use 1% of the progress for the XML
}
else
{
// no disks to export:
m->ulWeightForXmlOperation = 1;
}
switch (mode)
{
case ImportFile:
{
break;
}
case WriteFile:
{
// assume that creating the manifest will take .1% of the time it takes to export the disks
if (m->fManifest)
{
++cOperations; // another one for creating the manifest
m->ulWeightForManifestOperation = (ULONG)((double)m->ulTotalDisksMB * .1 / 100); // use .5% of the progress for the manifest
}
break;
}
case ImportS3:
{
if (!m->ulTotalDisksMB)
// no disks to export:
break;
}
case WriteS3:
{
if (m->ulTotalDisksMB)
{
m->ulWeightForXmlOperation = (ULONG)((double)m->ulTotalDisksMB * 1 / 100); // use 1% of the progress for OVF file upload (we didn't know the size at this point)
}
else
{
// no disks to export:
m->ulWeightForXmlOperation = 1;
}
ULONG ulOVFCreationWeight = (ULONG)((double)ulTotalOperationsWeight * 50.0 / 100.0); /* Use 50% for the creation of the OVF & the disks */
break;
}
}
Log(("Setting up progress object: ulTotalMB = %d, cDisks = %d, => cOperations = %d, ulTotalOperationsWeight = %d, m->ulWeightForXmlOperation = %d\n",
TRUE /* aCancelable */,
cOperations, // ULONG cOperations,
ulTotalOperationsWeight, // ULONG ulTotalOperationsWeight,
m->ulWeightForXmlOperation); // ULONG ulFirstOperationWeight,
return rc;
}
/**
* Called from the import and export background threads to synchronize the second
* background disk thread's progress object with the current progress object so
* that the user interface sees progress correctly and that cancel signals are
* passed on to the second thread.
* @param pProgressThis Progress object of the current thread.
* @param pProgressAsync Progress object of asynchronous task running in background.
*/
{
// now loop until the asynchronous operation completes and then report its result
{
if (fCanceled)
pProgressAsync->Cancel();
/* Check if the current operation has changed. It is also possible
that in the meantime more than one async operation was finished. So
we have to loop as long as we reached the same operation count. */
for (;;)
{
{
++cOp;
}
else
break;
}
if (fCompleted)
break;
/* Make sure the loop is not too tight */
}
// report result of asynchronous operation
// if the thread of the progress object has an error, then
// retrieve the error info from there, or it'll be lost
{
throw rc2;
}
}
{
}
/**
* Refreshes the cDisks and ulTotalDisksMB members in the instance data.
* Requires that virtual system descriptions are present.
*/
void Appliance::i_disksWeight()
{
m->ulTotalDisksMB = 0;
m->cDisks = 0;
// weigh the disk images according to their sizes
++it)
{
/* One for every hard disk of the Virtual System */
std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->i_findByType(VirtualSystemDescriptionType_HardDiskImage);
++itH)
{
++m->cDisks;
}
++itH)
{
++m->cDisks;
}
}
}
{
/* Buckets are S3 specific. So parse the bucket out of the file path */
throw setError(E_INVALIDARG,
{
}
/* If there is no bucket name provided reject it */
throw setError(E_INVALIDARG,
}
/**
*
* @return
*/
{
"Appliance::Task");
if (RT_FAILURE(vrc))
return S_OK;
}
/**
* Thread function for the thread started in Appliance::readImpl() and Appliance::importImpl()
* and Appliance::writeImpl().
* This will in turn call Appliance::readFS() or Appliance::readS3() or Appliance::importFS()
* or Appliance::importS3() or Appliance::writeFS() or Appliance::writeS3().
*
* @param aThread
* @param pvUser
*/
/* static */
{
{
#ifdef VBOX_WITH_S3
#else
#endif
break;
#ifdef VBOX_WITH_S3
#else
#endif
break;
#ifdef VBOX_WITH_S3
#else
#endif
break;
}
return VINF_SUCCESS;
}
/* static */
{
if ( pTask
{
if (fCanceled)
return -1;
}
return VINF_SUCCESS;
}
{
/* Check the URI for the protocol */
{
}
{
}
{
}
throw E_NOTIMPL;
/* Not necessary on a file based URI */
{
{
{
}
}
{
}
}
}
////////////////////////////////////////////////////////////////////////////////
//
// IVirtualSystemDescription constructor / destructor
//
////////////////////////////////////////////////////////////////////////////////
/**
* COM initializer.
* @return
*/
{
/* Enclose the state transition NotReady->InInit->Ready */
AutoInitSpan autoInitSpan(this);
/* Initialize data */
m = new Data();
/* Confirm a successful initialization */
return S_OK;
}
/**
* COM uninitializer.
*/
void VirtualSystemDescription::uninit()
{
if (m->pConfig)
delete m->pConfig;
delete m;
m = NULL;
}
////////////////////////////////////////////////////////////////////////////////
//
// IVirtualSystemDescription public methods
//
////////////////////////////////////////////////////////////////////////////////
/**
* Public method implementation.
* @param
* @return
*/
{
if (!aCount)
return E_POINTER;
return S_OK;
}
/**
* Public method implementation.
* @return
*/
HRESULT VirtualSystemDescription::getDescription(std::vector<VirtualSystemDescriptionType_T> &aTypes,
{
aOVFValues.resize(c);
aVBoxValues.resize(c);
for (size_t i = 0; i < c; i++)
{
}
return S_OK;
}
/**
* Public method implementation.
* @return
*/
{
aOVFValues.resize(c);
aVBoxValues.resize(c);
size_t i = 0;
for (list<VirtualSystemDescriptionEntry*>::const_iterator it = vsd.begin(); it != vsd.end(); ++it, ++i)
{
}
return S_OK;
}
/**
* Public method implementation.
* @return
*/
{
size_t i = 0;
++it, ++i)
{
switch (aWhich)
{
case VirtualSystemDescriptionValueType_ExtraConfig: aValues[i] = vsde->strExtraConfigCurrent; break;
}
}
return S_OK;
}
/**
* Public method implementation.
* @return
*/
{
#ifndef RT_OS_WINDOWS
// NOREF(aEnabledSize);
#endif /* RT_OS_WINDOWS */
)
return E_INVALIDARG;
size_t i = 0;
++it, ++i)
{
if (aEnabled[i])
{
}
else
}
return S_OK;
}
/**
* Public method implementation.
* @return
*/
{
return S_OK;
}
/**
* Internal method; adds a new description item to the member list.
* @param aType Type of description for the new item.
* @param strRef Reference item; only used with hard disk controllers.
* @param aOrigValue Corresponding original value from OVF.
* @param aAutoValue Initial configuration value (can be overridden by caller with setFinalValues).
* @param ulSizeMB Weight for IProgress
* @param strExtraConfig Extra configuration; meaning dependent on type.
*/
const Utf8Str &aVboxValue,
{
vsde.ulIndex = (uint32_t)m->maDescriptions.size(); // each entry gets an index so the client side can reference them
= aVboxValue;
}
/**
* Private method; returns a list of description items containing all the items from the member
* description items of this virtual system that match the given type.
* @param aType
* @return
*/
std::list<VirtualSystemDescriptionEntry*> VirtualSystemDescription::i_findByType(VirtualSystemDescriptionType_T aType)
{
++it)
{
}
return vsd;
}
/* Private method; delete all records from the list
* m->llDescriptions that match the given type.
* @param aType
* @return
*/
{
{
else
++it;
}
}
/**
* Private method; looks thru the member hardware items for the IDE, SATA, or SCSI controller with
* the given reference ID. Useful when needing the controller for a particular
* virtual disk.
* @param id
* @return
*/
{
++it)
{
const VirtualSystemDescriptionEntry &d = *it;
switch (d.type)
{
return &d;
break;
}
}
return NULL;
}
/**
* Method called from Appliance::Interpret() if the source OVF for a virtual system
* contains a <vbox:Machine> element. This method then attempts to parse that and
* create a MachineConfigFile instance from it which is stored in this instance data
* and can then be used to create a machine.
*
* This must only be called once per instance.
*
* This rethrows all XML and logic errors from MachineConfigFile.
*
* @param elmMachine <vbox:Machine> element with attributes and subelements from some
* DOM tree.
*/
{
try
{
}
catch (...)
{
if (pConfig)
delete pConfig;
throw;
}
}
/**
* Returns the machine config created by importVboxMachineXML() or NULL if there's none.
* @return
*/
{
return m->pConfig;
}