ApplianceImpl.cpp revision 15e9181ff43bf09ce40eaab73cee8c8492f99878
/* $Id$ */
/** @file
*
* IAppliance and IVirtualSystem COM class implementations.
*/
/*
* Copyright (C) 2008-2010 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 "ApplianceImpl.h"
#include "VFSExplorerImpl.h"
#include "VirtualBoxImpl.h"
#include "GuestOSTypeImpl.h"
#include "ProgressImpl.h"
#include "MachineImpl.h"
#include "AutoCaller.h"
#include "Logging.h"
#include "ApplianceImplPrivate.h"
using namespace std;
////////////////////////////////////////////////////////////////////////////////
//
// Internal helpers
//
////////////////////////////////////////////////////////////////////////////////
static const struct
{
const char *pcszVbox;
}
g_osTypes[] =
{
{ ovf::CIMOSType_CIMOS_MACOS, SchemaDefs_OSTypeId_MacOS_64 }, // there is no CIM 64-bit type for this
// Linuxes
// types that we have support for but CIM doesnt
// types added with CIM 2.25.0 follow:
{ ovf::CIMOSType_CIMOS_WindowsServer2008R2, SchemaDefs_OSTypeId_Windows2008 }, // duplicate, see above
// { ovf::CIMOSType_CIMOS_VMwareESXi = 104, // we can't run ESX in a VM
{ ovf::CIMOSType_CIMOS_Windows7, SchemaDefs_OSTypeId_Windows7_64 }, // there is no CIM 64-bit type for this
// there are no CIM types for these, so these turn to "other" on export:
// SchemaDefs_OSTypeId_OpenBSD
// SchemaDefs_OSTypeId_OpenBSD_64
// SchemaDefs_OSTypeId_NetBSD
// SchemaDefs_OSTypeId_NetBSD_64
};
/* Pattern structure for matching the OS type description field */
struct osTypePattern
{
const char *pcszPattern;
const char *pcszVbox;
};
/* These are the 32-Bit ones. They are sorted by priority. */
static const osTypePattern g_osTypesPattern[] =
{
{"Windows NT", SchemaDefs_OSTypeId_WindowsNT4},
{"Windows XP", SchemaDefs_OSTypeId_WindowsXP},
{"Windows 2000", SchemaDefs_OSTypeId_Windows2000},
{"Windows 2003", SchemaDefs_OSTypeId_Windows2003},
{"Windows Vista", SchemaDefs_OSTypeId_WindowsVista},
{"Windows 2008", SchemaDefs_OSTypeId_Windows2008},
{"SUSE", SchemaDefs_OSTypeId_OpenSUSE},
{"Novell", SchemaDefs_OSTypeId_OpenSUSE},
{"Red Hat", SchemaDefs_OSTypeId_RedHat},
{"Mandriva", SchemaDefs_OSTypeId_Mandriva},
{"Ubuntu", SchemaDefs_OSTypeId_Ubuntu},
{"Debian", SchemaDefs_OSTypeId_Debian},
{"QNX", SchemaDefs_OSTypeId_QNX},
{"Linux 2.4", SchemaDefs_OSTypeId_Linux24},
{"Linux 2.6", SchemaDefs_OSTypeId_Linux26},
{"Linux", SchemaDefs_OSTypeId_Linux},
{"OpenSolaris", SchemaDefs_OSTypeId_OpenSolaris},
{"Solaris", SchemaDefs_OSTypeId_OpenSolaris},
{"FreeBSD", SchemaDefs_OSTypeId_FreeBSD},
{"NetBSD", SchemaDefs_OSTypeId_NetBSD},
{"Windows 95", SchemaDefs_OSTypeId_Windows95},
{"Windows 98", SchemaDefs_OSTypeId_Windows98},
{"Windows Me", SchemaDefs_OSTypeId_WindowsMe},
{"Windows 3.", SchemaDefs_OSTypeId_Windows31},
{"DOS", SchemaDefs_OSTypeId_DOS},
{"OS2", SchemaDefs_OSTypeId_OS2}
};
/* These are the 64-Bit ones. They are sorted by priority. */
static const osTypePattern g_osTypesPattern64[] =
{
{"Windows XP", SchemaDefs_OSTypeId_WindowsXP_64},
{"Windows 2003", SchemaDefs_OSTypeId_Windows2003_64},
{"Windows Vista", SchemaDefs_OSTypeId_WindowsVista_64},
{"Windows 2008", SchemaDefs_OSTypeId_Windows2008_64},
{"SUSE", SchemaDefs_OSTypeId_OpenSUSE_64},
{"Novell", SchemaDefs_OSTypeId_OpenSUSE_64},
{"Red Hat", SchemaDefs_OSTypeId_RedHat_64},
{"Mandriva", SchemaDefs_OSTypeId_Mandriva_64},
{"Ubuntu", SchemaDefs_OSTypeId_Ubuntu_64},
{"Debian", SchemaDefs_OSTypeId_Debian_64},
{"Linux 2.4", SchemaDefs_OSTypeId_Linux24_64},
{"Linux 2.6", SchemaDefs_OSTypeId_Linux26_64},
{"Linux", SchemaDefs_OSTypeId_Linux26_64},
{"OpenSolaris", SchemaDefs_OSTypeId_OpenSolaris_64},
{"Solaris", SchemaDefs_OSTypeId_OpenSolaris_64},
{"FreeBSD", SchemaDefs_OSTypeId_FreeBSD_64},
};
/**
* 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;
}
}
}
/**
* Private helper func that suggests a VirtualBox guest OS type
* for the given OVF operating system type.
* @param osTypeVBox
* @param c
*/
{
{
}
return ovf::CIMOSType_CIMOS_Other;
}
////////////////////////////////////////////////////////////////////////////////
//
// 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 constructor / destructor
//
////////////////////////////////////////////////////////////////////////////////
: mVirtualBox(NULL)
{
}
{
}
/**
* 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 S_OK;
}
/**
* 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 (!aPath)
return E_POINTER;
AutoCaller autoCaller(this);
if (!isApplianceIdle())
return E_ACCESSDENIED;
return S_OK;
}
/**
* Public method implementation.
* @param
* @return
*/
{
AutoCaller autoCaller(this);
if (!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());
// push to safearray
}
}
return S_OK;
}
/**
* Public method implementation.
* @param
* @return
*/
STDMETHODIMP Appliance::COMGETTER(VirtualSystemDescriptions)(ComSafeArrayOut(IVirtualSystemDescription*, aVirtualSystemDescriptions))
{
AutoCaller autoCaller(this);
if (!isApplianceIdle())
return E_ACCESSDENIED;
return S_OK;
}
{
AutoCaller autoCaller(this);
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
*/
{
return E_POINTER;
AutoCaller autoCaller(this);
size_t i = 0;
++it, ++i)
{
}
return S_OK;
}
////////////////////////////////////////////////////////////////////////////////
//
// Appliance private methods
//
////////////////////////////////////////////////////////////////////////////////
/**
* 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::isApplianceIdle() const
{
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)
)
{
++i;
}
return S_OK;
}
/**
* Little shortcut to SystemProperties::DefaultHardDiskFolder.
* @param str
* @return
*/
{
/* We need the default path for storing disk images */
return S_OK;
}
/**
* 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();
break;
}
if (!pProgressThis.isNull())
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::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->findByType(VirtualSystemDescriptionType_HardDiskImage);
++itH)
{
++m->cDisks;
}
}
}
/**
* 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)
disksWeight();
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 ImportFileNoManifest:
break;
case ImportFileWithManifest:
case WriteFile:
++cOperations; // another one for creating the manifest
// assume that checking or creating the manifest will take 10% of the time it takes to export the disks
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,
bstrDescription, // CBSTR bstrFirstOperationDescription,
m->ulWeightForXmlOperation); // ULONG ulFirstOperationWeight,
return rc;
}
{
/* Check the URI for the protocol */
{
}
{
}
{
}
throw E_NOTIMPL;
/* Not necessary on a file based URI */
{
{
{
}
}
{
}
}
}
{
/* 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,
}
{
/* Get the name part */
/* Strip any extensions */
/* Path without the filename */
/* Format the manifest path */
return strMfFile;
}
/**
*
* @return
*/
{
"Appliance::Task");
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 */
{
{
break;
break;
break;
}
return VINF_SUCCESS;
}
/* static */
{
if ( pTask
{
if (fCanceled)
return -1;
}
return VINF_SUCCESS;
}
////////////////////////////////////////////////////////////////////////////////
//
// 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;
AutoCaller autoCaller(this);
return S_OK;
}
/**
* Public method implementation.
* @return
*/
STDMETHODIMP VirtualSystemDescription::GetDescription(ComSafeArrayOut(VirtualSystemDescriptionType_T, aTypes),
{
if (ComSafeArrayOutIsNull(aTypes) ||
return E_POINTER;
AutoCaller autoCaller(this);
size_t i = 0;
++it, ++i)
{
}
return S_OK;
}
/**
* Public method implementation.
* @return
*/
{
if (ComSafeArrayOutIsNull(aTypes) ||
return E_POINTER;
AutoCaller autoCaller(this);
size_t i = 0;
++it, ++i)
{
}
return S_OK;
}
/**
* Public method implementation.
* @return
*/
{
if (ComSafeArrayOutIsNull(aValues))
return E_POINTER;
AutoCaller autoCaller(this);
size_t i = 0;
++it, ++i)
{
switch (aWhich)
{
}
}
return S_OK;
}
/**
* Public method implementation.
* @return
*/
{
#ifndef RT_OS_WINDOWS
#endif /* RT_OS_WINDOWS */
AutoCaller autoCaller(this);
)
return E_INVALIDARG;
size_t i = 0;
++it, ++i)
{
if (sfaEnabled[i])
{
}
else
}
return S_OK;
}
/**
* Public method implementation.
* @return
*/
{
AutoCaller autoCaller(this);
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->llDescriptions.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::findByType(VirtualSystemDescriptionType_T aType)
{
++it)
{
}
return vsd;
}
/**
* 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;
}