VirtualBox

source: vbox/trunk/src/VBox/Main/ApplianceImplImport.cpp@ 27900

Last change on this file since 27900 was 27900, checked in by vboxsync, 15 years ago

Main/OVF: move another method

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 85.9 KB
Line 
1/* $Id: ApplianceImplImport.cpp 27900 2010-03-31 14:07:18Z vboxsync $ */
2/** @file
3 *
4 * IAppliance and IVirtualSystem COM class implementations.
5 */
6
7/*
8 * Copyright (C) 2008-2010 Sun Microsystems, Inc.
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
19 * Clara, CA 95054 USA or visit http://www.sun.com if you need
20 * additional information or have any questions.
21 */
22
23#include <iprt/path.h>
24#include <iprt/dir.h>
25#include <iprt/file.h>
26#include <iprt/s3.h>
27#include <iprt/sha.h>
28#include <iprt/manifest.h>
29
30#include <VBox/com/array.h>
31
32#include "ApplianceImpl.h"
33#include "VirtualBoxImpl.h"
34#include "GuestOSTypeImpl.h"
35#include "ProgressImpl.h"
36
37#include "AutoCaller.h"
38#include "Logging.h"
39
40#include "ApplianceImplPrivate.h"
41
42#include <VBox/param.h>
43#include <VBox/version.h>
44
45using namespace std;
46
47////////////////////////////////////////////////////////////////////////////////
48//
49// IAppliance public methods
50//
51////////////////////////////////////////////////////////////////////////////////
52
53/**
54 * Public method implementation.
55 * @param path
56 * @return
57 */
58STDMETHODIMP Appliance::Read(IN_BSTR path, IProgress **aProgress)
59{
60 if (!path) return E_POINTER;
61 CheckComArgOutPointerValid(aProgress);
62
63 AutoCaller autoCaller(this);
64 if (FAILED(autoCaller.rc())) return autoCaller.rc();
65
66 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
67
68 if (!isApplianceIdle())
69 return E_ACCESSDENIED;
70
71 if (m->pReader)
72 {
73 delete m->pReader;
74 m->pReader = NULL;
75 }
76
77 // see if we can handle this file; for now we insist it has an ".ovf" extension
78 Utf8Str strPath (path);
79 if (!strPath.endsWith(".ovf", Utf8Str::CaseInsensitive))
80 return setError(VBOX_E_FILE_ERROR,
81 tr("Appliance file must have .ovf extension"));
82
83 ComObjPtr<Progress> progress;
84 HRESULT rc = S_OK;
85 try
86 {
87 /* Parse all necessary info out of the URI */
88 parseURI(strPath, m->locInfo);
89 rc = readImpl(m->locInfo, progress);
90 }
91 catch (HRESULT aRC)
92 {
93 rc = aRC;
94 }
95
96 if (SUCCEEDED(rc))
97 /* Return progress to the caller */
98 progress.queryInterfaceTo(aProgress);
99
100 return S_OK;
101}
102
103/**
104 * Public method implementation.
105 * @return
106 */
107STDMETHODIMP Appliance::Interpret()
108{
109 // @todo:
110 // - don't use COM methods but the methods directly (faster, but needs appropriate locking of that objects itself (s. HardDisk))
111 // - Appropriate handle errors like not supported file formats
112 AutoCaller autoCaller(this);
113 if (FAILED(autoCaller.rc())) return autoCaller.rc();
114
115 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
116
117 if (!isApplianceIdle())
118 return E_ACCESSDENIED;
119
120 HRESULT rc = S_OK;
121
122 /* Clear any previous virtual system descriptions */
123 m->virtualSystemDescriptions.clear();
124
125 /* We need the default path for storing disk images */
126 ComPtr<ISystemProperties> systemProps;
127 rc = mVirtualBox->COMGETTER(SystemProperties)(systemProps.asOutParam());
128 if (FAILED(rc)) return rc;
129 Bstr bstrDefaultHardDiskLocation;
130 rc = systemProps->COMGETTER(DefaultHardDiskFolder)(bstrDefaultHardDiskLocation.asOutParam());
131 if (FAILED(rc)) return rc;
132
133 if (!m->pReader)
134 return setError(E_FAIL,
135 tr("Cannot interpret appliance without reading it first (call read() before interpret())"));
136
137 // Change the appliance state so we can safely leave the lock while doing time-consuming
138 // disk imports; also the below method calls do all kinds of locking which conflicts with
139 // the appliance object lock
140 m->state = Data::ApplianceImporting;
141 alock.release();
142
143 /* Try/catch so we can clean up on error */
144 try
145 {
146 list<VirtualSystem>::const_iterator it;
147 /* Iterate through all virtual systems */
148 for (it = m->pReader->m_llVirtualSystems.begin();
149 it != m->pReader->m_llVirtualSystems.end();
150 ++it)
151 {
152 const VirtualSystem &vsysThis = *it;
153
154 ComObjPtr<VirtualSystemDescription> pNewDesc;
155 rc = pNewDesc.createObject();
156 if (FAILED(rc)) throw rc;
157 rc = pNewDesc->init();
158 if (FAILED(rc)) throw rc;
159
160 /* Guest OS type */
161 Utf8Str strOsTypeVBox,
162 strCIMOSType = Utf8StrFmt("%RI32", (uint32_t)vsysThis.cimos);
163 convertCIMOSType2VBoxOSType(strOsTypeVBox, vsysThis.cimos, vsysThis.strCimosDesc);
164 pNewDesc->addEntry(VirtualSystemDescriptionType_OS,
165 "",
166 strCIMOSType,
167 strOsTypeVBox);
168
169 /* VM name */
170 /* If the there isn't any name specified create a default one out of
171 * the OS type */
172 Utf8Str nameVBox = vsysThis.strName;
173 if (nameVBox.isEmpty())
174 nameVBox = strOsTypeVBox;
175 searchUniqueVMName(nameVBox);
176 pNewDesc->addEntry(VirtualSystemDescriptionType_Name,
177 "",
178 vsysThis.strName,
179 nameVBox);
180
181 /* VM Product */
182 if (!vsysThis.strProduct.isEmpty())
183 pNewDesc->addEntry(VirtualSystemDescriptionType_Product,
184 "",
185 vsysThis.strProduct,
186 vsysThis.strProduct);
187
188 /* VM Vendor */
189 if (!vsysThis.strVendor.isEmpty())
190 pNewDesc->addEntry(VirtualSystemDescriptionType_Vendor,
191 "",
192 vsysThis.strVendor,
193 vsysThis.strVendor);
194
195 /* VM Version */
196 if (!vsysThis.strVersion.isEmpty())
197 pNewDesc->addEntry(VirtualSystemDescriptionType_Version,
198 "",
199 vsysThis.strVersion,
200 vsysThis.strVersion);
201
202 /* VM ProductUrl */
203 if (!vsysThis.strProductUrl.isEmpty())
204 pNewDesc->addEntry(VirtualSystemDescriptionType_ProductUrl,
205 "",
206 vsysThis.strProductUrl,
207 vsysThis.strProductUrl);
208
209 /* VM VendorUrl */
210 if (!vsysThis.strVendorUrl.isEmpty())
211 pNewDesc->addEntry(VirtualSystemDescriptionType_VendorUrl,
212 "",
213 vsysThis.strVendorUrl,
214 vsysThis.strVendorUrl);
215
216 /* VM description */
217 if (!vsysThis.strDescription.isEmpty())
218 pNewDesc->addEntry(VirtualSystemDescriptionType_Description,
219 "",
220 vsysThis.strDescription,
221 vsysThis.strDescription);
222
223 /* VM license */
224 if (!vsysThis.strLicenseText.isEmpty())
225 pNewDesc->addEntry(VirtualSystemDescriptionType_License,
226 "",
227 vsysThis.strLicenseText,
228 vsysThis.strLicenseText);
229
230 /* Now that we know the OS type, get our internal defaults based on that. */
231 ComPtr<IGuestOSType> pGuestOSType;
232 rc = mVirtualBox->GetGuestOSType(Bstr(strOsTypeVBox), pGuestOSType.asOutParam());
233 if (FAILED(rc)) throw rc;
234
235 /* CPU count */
236 ULONG cpuCountVBox = vsysThis.cCPUs;
237 /* Check for the constrains */
238 if (cpuCountVBox > SchemaDefs::MaxCPUCount)
239 {
240 addWarning(tr("The virtual system \"%s\" claims support for %u CPU's, but VirtualBox has support for max %u CPU's only."),
241 vsysThis.strName.c_str(), cpuCountVBox, SchemaDefs::MaxCPUCount);
242 cpuCountVBox = SchemaDefs::MaxCPUCount;
243 }
244 if (vsysThis.cCPUs == 0)
245 cpuCountVBox = 1;
246 pNewDesc->addEntry(VirtualSystemDescriptionType_CPU,
247 "",
248 Utf8StrFmt("%RI32", (uint32_t)vsysThis.cCPUs),
249 Utf8StrFmt("%RI32", (uint32_t)cpuCountVBox));
250
251 /* RAM */
252 uint64_t ullMemSizeVBox = vsysThis.ullMemorySize / _1M;
253 /* Check for the constrains */
254 if ( ullMemSizeVBox != 0
255 && ( ullMemSizeVBox < MM_RAM_MIN_IN_MB
256 || ullMemSizeVBox > MM_RAM_MAX_IN_MB)
257 )
258 {
259 addWarning(tr("The virtual system \"%s\" claims support for %llu MB RAM size, but VirtualBox has support for min %u & max %u MB RAM size only."),
260 vsysThis.strName.c_str(), ullMemSizeVBox, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
261 ullMemSizeVBox = RT_MIN(RT_MAX(ullMemSizeVBox, MM_RAM_MIN_IN_MB), MM_RAM_MAX_IN_MB);
262 }
263 if (vsysThis.ullMemorySize == 0)
264 {
265 /* If the RAM of the OVF is zero, use our predefined values */
266 ULONG memSizeVBox2;
267 rc = pGuestOSType->COMGETTER(RecommendedRAM)(&memSizeVBox2);
268 if (FAILED(rc)) throw rc;
269 /* VBox stores that in MByte */
270 ullMemSizeVBox = (uint64_t)memSizeVBox2;
271 }
272 pNewDesc->addEntry(VirtualSystemDescriptionType_Memory,
273 "",
274 Utf8StrFmt("%RI64", (uint64_t)vsysThis.ullMemorySize),
275 Utf8StrFmt("%RI64", (uint64_t)ullMemSizeVBox));
276
277 /* Audio */
278 if (!vsysThis.strSoundCardType.isEmpty())
279 /* Currently we set the AC97 always.
280 @todo: figure out the hardware which could be possible */
281 pNewDesc->addEntry(VirtualSystemDescriptionType_SoundCard,
282 "",
283 vsysThis.strSoundCardType,
284 Utf8StrFmt("%RI32", (uint32_t)AudioControllerType_AC97));
285
286#ifdef VBOX_WITH_USB
287 /* USB Controller */
288 if (vsysThis.fHasUsbController)
289 pNewDesc->addEntry(VirtualSystemDescriptionType_USBController, "", "", "");
290#endif /* VBOX_WITH_USB */
291
292 /* Network Controller */
293 size_t cEthernetAdapters = vsysThis.llEthernetAdapters.size();
294 if (cEthernetAdapters > 0)
295 {
296 /* Check for the constrains */
297 if (cEthernetAdapters > SchemaDefs::NetworkAdapterCount)
298 addWarning(tr("The virtual system \"%s\" claims support for %zu network adapters, but VirtualBox has support for max %u network adapter only."),
299 vsysThis.strName.c_str(), cEthernetAdapters, SchemaDefs::NetworkAdapterCount);
300
301 /* Get the default network adapter type for the selected guest OS */
302 NetworkAdapterType_T defaultAdapterVBox = NetworkAdapterType_Am79C970A;
303 rc = pGuestOSType->COMGETTER(AdapterType)(&defaultAdapterVBox);
304 if (FAILED(rc)) throw rc;
305
306 EthernetAdaptersList::const_iterator itEA;
307 /* Iterate through all abstract networks. We support 8 network
308 * adapters at the maximum, so the first 8 will be added only. */
309 size_t a = 0;
310 for (itEA = vsysThis.llEthernetAdapters.begin();
311 itEA != vsysThis.llEthernetAdapters.end() && a < SchemaDefs::NetworkAdapterCount;
312 ++itEA, ++a)
313 {
314 const EthernetAdapter &ea = *itEA; // logical network to connect to
315 Utf8Str strNetwork = ea.strNetworkName;
316 // make sure it's one of these two
317 if ( (strNetwork.compare("Null", Utf8Str::CaseInsensitive))
318 && (strNetwork.compare("NAT", Utf8Str::CaseInsensitive))
319 && (strNetwork.compare("Bridged", Utf8Str::CaseInsensitive))
320 && (strNetwork.compare("Internal", Utf8Str::CaseInsensitive))
321 && (strNetwork.compare("HostOnly", Utf8Str::CaseInsensitive))
322 )
323 strNetwork = "Bridged"; // VMware assumes this is the default apparently
324
325 /* Figure out the hardware type */
326 NetworkAdapterType_T nwAdapterVBox = defaultAdapterVBox;
327 if (!ea.strAdapterType.compare("PCNet32", Utf8Str::CaseInsensitive))
328 {
329 /* If the default adapter is already one of the two
330 * PCNet adapters use the default one. If not use the
331 * Am79C970A as fallback. */
332 if (!(defaultAdapterVBox == NetworkAdapterType_Am79C970A ||
333 defaultAdapterVBox == NetworkAdapterType_Am79C973))
334 nwAdapterVBox = NetworkAdapterType_Am79C970A;
335 }
336#ifdef VBOX_WITH_E1000
337 /* VMWare accidentally write this with VirtualCenter 3.5,
338 so make sure in this case always to use the VMWare one */
339 else if (!ea.strAdapterType.compare("E10000", Utf8Str::CaseInsensitive))
340 nwAdapterVBox = NetworkAdapterType_I82545EM;
341 else if (!ea.strAdapterType.compare("E1000", Utf8Str::CaseInsensitive))
342 {
343 /* Check if this OVF was written by VirtualBox */
344 if (Utf8Str(vsysThis.strVirtualSystemType).contains("virtualbox", Utf8Str::CaseInsensitive))
345 {
346 /* If the default adapter is already one of the three
347 * E1000 adapters use the default one. If not use the
348 * I82545EM as fallback. */
349 if (!(defaultAdapterVBox == NetworkAdapterType_I82540EM ||
350 defaultAdapterVBox == NetworkAdapterType_I82543GC ||
351 defaultAdapterVBox == NetworkAdapterType_I82545EM))
352 nwAdapterVBox = NetworkAdapterType_I82540EM;
353 }
354 else
355 /* Always use this one since it's what VMware uses */
356 nwAdapterVBox = NetworkAdapterType_I82545EM;
357 }
358#endif /* VBOX_WITH_E1000 */
359
360 pNewDesc->addEntry(VirtualSystemDescriptionType_NetworkAdapter,
361 "", // ref
362 ea.strNetworkName, // orig
363 Utf8StrFmt("%RI32", (uint32_t)nwAdapterVBox), // conf
364 0,
365 Utf8StrFmt("type=%s", strNetwork.c_str())); // extra conf
366 }
367 }
368
369 /* Floppy Drive */
370 if (vsysThis.fHasFloppyDrive)
371 pNewDesc->addEntry(VirtualSystemDescriptionType_Floppy, "", "", "");
372
373 /* CD Drive */
374 if (vsysThis.fHasCdromDrive)
375 pNewDesc->addEntry(VirtualSystemDescriptionType_CDROM, "", "", "");
376
377 /* Hard disk Controller */
378 uint16_t cIDEused = 0;
379 uint16_t cSATAused = 0; NOREF(cSATAused);
380 uint16_t cSCSIused = 0; NOREF(cSCSIused);
381 ControllersMap::const_iterator hdcIt;
382 /* Iterate through all hard disk controllers */
383 for (hdcIt = vsysThis.mapControllers.begin();
384 hdcIt != vsysThis.mapControllers.end();
385 ++hdcIt)
386 {
387 const HardDiskController &hdc = hdcIt->second;
388 Utf8Str strControllerID = Utf8StrFmt("%RI32", (uint32_t)hdc.idController);
389
390 switch (hdc.system)
391 {
392 case HardDiskController::IDE:
393 {
394 /* Check for the constrains */
395 /* @todo: I'm very confused! Are these bits *one* controller or
396 is every port/bus declared as an extra controller. */
397 if (cIDEused < 4)
398 {
399 // @todo: figure out the IDE types
400 /* Use PIIX4 as default */
401 Utf8Str strType = "PIIX4";
402 if (!hdc.strControllerType.compare("PIIX3", Utf8Str::CaseInsensitive))
403 strType = "PIIX3";
404 else if (!hdc.strControllerType.compare("ICH6", Utf8Str::CaseInsensitive))
405 strType = "ICH6";
406 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerIDE,
407 strControllerID,
408 hdc.strControllerType,
409 strType);
410 }
411 else
412 {
413 /* Warn only once */
414 if (cIDEused == 1)
415 addWarning(tr("The virtual \"%s\" system requests support for more than one IDE controller, but VirtualBox has support for only one."),
416 vsysThis.strName.c_str());
417
418 }
419 ++cIDEused;
420 break;
421 }
422
423 case HardDiskController::SATA:
424 {
425#ifdef VBOX_WITH_AHCI
426 /* Check for the constrains */
427 if (cSATAused < 1)
428 {
429 // @todo: figure out the SATA types
430 /* We only support a plain AHCI controller, so use them always */
431 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSATA,
432 strControllerID,
433 hdc.strControllerType,
434 "AHCI");
435 }
436 else
437 {
438 /* Warn only once */
439 if (cSATAused == 1)
440 addWarning(tr("The virtual system \"%s\" requests support for more than one SATA controller, but VirtualBox has support for only one"),
441 vsysThis.strName.c_str());
442
443 }
444 ++cSATAused;
445 break;
446#else /* !VBOX_WITH_AHCI */
447 addWarning(tr("The virtual system \"%s\" requests at least one SATA controller but this version of VirtualBox does not provide a SATA controller emulation"),
448 vsysThis.strName.c_str());
449#endif /* !VBOX_WITH_AHCI */
450 }
451
452 case HardDiskController::SCSI:
453 {
454#ifdef VBOX_WITH_LSILOGIC
455 /* Check for the constrains */
456 if (cSCSIused < 1)
457 {
458 Utf8Str hdcController = "LsiLogic";
459 if (!hdc.strControllerType.compare("BusLogic", Utf8Str::CaseInsensitive))
460 hdcController = "BusLogic";
461 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSCSI,
462 strControllerID,
463 hdc.strControllerType,
464 hdcController);
465 }
466 else
467 addWarning(tr("The virtual system \"%s\" requests support for an additional SCSI controller of type \"%s\" with ID %s, but VirtualBox presently supports only one SCSI controller."),
468 vsysThis.strName.c_str(),
469 hdc.strControllerType.c_str(),
470 strControllerID.c_str());
471 ++cSCSIused;
472 break;
473#else /* !VBOX_WITH_LSILOGIC */
474 addWarning(tr("The virtual system \"%s\" requests at least one SATA controller but this version of VirtualBox does not provide a SCSI controller emulation"),
475 vsysThis.strName.c_str());
476#endif /* !VBOX_WITH_LSILOGIC */
477 }
478 }
479 }
480
481 /* Hard disks */
482 if (vsysThis.mapVirtualDisks.size() > 0)
483 {
484 VirtualDisksMap::const_iterator itVD;
485 /* Iterate through all hard disks ()*/
486 for (itVD = vsysThis.mapVirtualDisks.begin();
487 itVD != vsysThis.mapVirtualDisks.end();
488 ++itVD)
489 {
490 const VirtualDisk &hd = itVD->second;
491 /* Get the associated disk image */
492 const DiskImage &di = m->pReader->m_mapDisks[hd.strDiskId];
493
494 // @todo:
495 // - figure out all possible vmdk formats we also support
496 // - figure out if there is a url specifier for vhd already
497 // - we need a url specifier for the vdi format
498 if ( di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#sparse", Utf8Str::CaseInsensitive)
499 || di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#compressed", Utf8Str::CaseInsensitive))
500 {
501 /* If the href is empty use the VM name as filename */
502 Utf8Str strFilename = di.strHref;
503 if (!strFilename.length())
504 strFilename = Utf8StrFmt("%s.vmdk", nameVBox.c_str());
505 /* Construct a unique target path */
506 Utf8StrFmt strPath("%ls%c%s",
507 bstrDefaultHardDiskLocation.raw(),
508 RTPATH_DELIMITER,
509 strFilename.c_str());
510 searchUniqueDiskImageFilePath(strPath);
511
512 /* find the description for the hard disk controller
513 * that has the same ID as hd.idController */
514 const VirtualSystemDescriptionEntry *pController;
515 if (!(pController = pNewDesc->findControllerFromID(hd.idController)))
516 throw setError(E_FAIL,
517 tr("Cannot find hard disk controller with OVF instance ID %RI32 to which disk \"%s\" should be attached"),
518 hd.idController,
519 di.strHref.c_str());
520
521 /* controller to attach to, and the bus within that controller */
522 Utf8StrFmt strExtraConfig("controller=%RI16;channel=%RI16",
523 pController->ulIndex,
524 hd.ulAddressOnParent);
525 ULONG ulSize = 0;
526 if (di.iCapacity != -1)
527 ulSize = (ULONG)(di.iCapacity / _1M);
528 else if (di.iPopulatedSize != -1)
529 ulSize = (ULONG)(di.iPopulatedSize / _1M);
530 else if (di.iSize != -1)
531 ulSize = (ULONG)(di.iSize / _1M);
532 if (ulSize == 0)
533 ulSize = 10000; // assume 10 GB, this is for the progress bar only anyway
534 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskImage,
535 hd.strDiskId,
536 di.strHref,
537 strPath,
538 ulSize,
539 strExtraConfig);
540 }
541 else
542 throw setError(VBOX_E_FILE_ERROR,
543 tr("Unsupported format for virtual disk image in OVF: \"%s\"", di.strFormat.c_str()));
544 }
545 }
546
547 m->virtualSystemDescriptions.push_back(pNewDesc);
548 }
549 }
550 catch (HRESULT aRC)
551 {
552 /* On error we clear the list & return */
553 m->virtualSystemDescriptions.clear();
554 rc = aRC;
555 }
556
557 // reset the appliance state
558 alock.acquire();
559 m->state = Data::ApplianceIdle;
560
561 return rc;
562}
563
564/**
565 * Public method implementation.
566 * @param aProgress
567 * @return
568 */
569STDMETHODIMP Appliance::ImportMachines(IProgress **aProgress)
570{
571 CheckComArgOutPointerValid(aProgress);
572
573 AutoCaller autoCaller(this);
574 if (FAILED(autoCaller.rc())) return autoCaller.rc();
575
576 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
577
578 // do not allow entering this method if the appliance is busy reading or writing
579 if (!isApplianceIdle())
580 return E_ACCESSDENIED;
581
582 if (!m->pReader)
583 return setError(E_FAIL,
584 tr("Cannot import machines without reading it first (call read() before importMachines())"));
585
586 ComObjPtr<Progress> progress;
587 HRESULT rc = S_OK;
588 try
589 {
590 rc = importImpl(m->locInfo, progress);
591 }
592 catch (HRESULT aRC)
593 {
594 rc = aRC;
595 }
596
597 if (SUCCEEDED(rc))
598 /* Return progress to the caller */
599 progress.queryInterfaceTo(aProgress);
600
601 return rc;
602}
603
604////////////////////////////////////////////////////////////////////////////////
605//
606// Appliance private methods
607//
608////////////////////////////////////////////////////////////////////////////////
609
610/**
611 * Implementation for reading an OVF. This starts a new thread which will call
612 * Appliance::taskThreadImportOVF() which will then call readFS() or readS3().
613 *
614 * This is in a separate private method because it is used from two locations:
615 *
616 * 1) from the public Appliance::Read().
617 * 2) from Appliance::readS3(), which got called from a previous instance of Appliance::taskThreadImportOVF().
618 *
619 * @param aLocInfo
620 * @param aProgress
621 * @return
622 */
623HRESULT Appliance::readImpl(const LocationInfo &aLocInfo, ComObjPtr<Progress> &aProgress)
624{
625 BstrFmt bstrDesc = BstrFmt(tr("Read appliance '%s'"),
626 aLocInfo.strPath.c_str());
627 HRESULT rc;
628 /* Create the progress object */
629 aProgress.createObject();
630 if (aLocInfo.storageType == VFSType_File)
631 /* 1 operation only */
632 rc = aProgress->init(mVirtualBox, static_cast<IAppliance*>(this),
633 bstrDesc,
634 TRUE /* aCancelable */);
635 else
636 /* 4/5 is downloading, 1/5 is reading */
637 rc = aProgress->init(mVirtualBox, static_cast<IAppliance*>(this),
638 bstrDesc,
639 TRUE /* aCancelable */,
640 2, // ULONG cOperations,
641 5, // ULONG ulTotalOperationsWeight,
642 BstrFmt(tr("Download appliance '%s'"),
643 aLocInfo.strPath.c_str()), // CBSTR bstrFirstOperationDescription,
644 4); // ULONG ulFirstOperationWeight,
645 if (FAILED(rc)) throw rc;
646
647 /* Initialize our worker task */
648 std::auto_ptr<TaskOVF> task(new TaskOVF(this, TaskOVF::Read, aLocInfo, aProgress));
649
650 rc = task->startThread();
651 if (FAILED(rc)) throw rc;
652
653 /* Don't destruct on success */
654 task.release();
655
656 return rc;
657}
658
659/**
660 * Actual worker code for reading an OVF from disk. This is called from Appliance::taskThreadImportOVF()
661 * and therefore runs on the OVF read worker thread. This runs in two contexts:
662 *
663 * 1) in a first worker thread; in that case, Appliance::Read() called Appliance::readImpl();
664 *
665 * 2) in a second worker thread; in that case, Appliance::Read() called Appliance::readImpl(), which
666 * called Appliance::readS3(), which called Appliance::readImpl(), which then called this.
667 *
668 * @param pTask
669 * @return
670 */
671HRESULT Appliance::readFS(const LocationInfo &locInfo)
672{
673 LogFlowFuncEnter();
674 LogFlowFunc(("Appliance %p\n", this));
675
676 AutoCaller autoCaller(this);
677 if (FAILED(autoCaller.rc())) return autoCaller.rc();
678
679 AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);
680
681 HRESULT rc = S_OK;
682
683 try
684 {
685 /* Read & parse the XML structure of the OVF file */
686 m->pReader = new OVFReader(locInfo.strPath);
687 /* Create the SHA1 sum of the OVF file for later validation */
688 char *pszDigest;
689 int vrc = RTSha1Digest(locInfo.strPath.c_str(), &pszDigest);
690 if (RT_FAILURE(vrc))
691 throw setError(VBOX_E_FILE_ERROR,
692 tr("Couldn't calculate SHA1 digest for file '%s' (%Rrc)"),
693 RTPathFilename(locInfo.strPath.c_str()), vrc);
694 m->strOVFSHA1Digest = pszDigest;
695 RTStrFree(pszDigest);
696 }
697 catch(xml::Error &x)
698 {
699 rc = setError(VBOX_E_FILE_ERROR,
700 x.what());
701 }
702 catch(HRESULT aRC)
703 {
704 rc = aRC;
705 }
706
707 LogFlowFunc(("rc=%Rhrc\n", rc));
708 LogFlowFuncLeave();
709
710 return rc;
711}
712
713/**
714 * Worker code for writing out OVF to the cloud. This is called from Appliance::taskThreadImportOVF()
715 * in S3 mode and therefore runs on the OVF read worker thread. This then starts a second worker
716 * thread to create temporary files (see Appliance::readFS()).
717 *
718 * @param pTask
719 * @return
720 */
721HRESULT Appliance::readS3(TaskOVF *pTask)
722{
723 LogFlowFuncEnter();
724 LogFlowFunc(("Appliance %p\n", this));
725
726 AutoCaller autoCaller(this);
727 if (FAILED(autoCaller.rc())) return autoCaller.rc();
728
729 AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);
730
731 HRESULT rc = S_OK;
732 int vrc = VINF_SUCCESS;
733 RTS3 hS3 = NIL_RTS3;
734 char szOSTmpDir[RTPATH_MAX];
735 RTPathTemp(szOSTmpDir, sizeof(szOSTmpDir));
736 /* The template for the temporary directory created below */
737 char *pszTmpDir;
738 RTStrAPrintf(&pszTmpDir, "%s"RTPATH_SLASH_STR"vbox-ovf-XXXXXX", szOSTmpDir);
739 list< pair<Utf8Str, ULONG> > filesList;
740 Utf8Str strTmpOvf;
741
742 try
743 {
744 /* Extract the bucket */
745 Utf8Str tmpPath = pTask->locInfo.strPath;
746 Utf8Str bucket;
747 parseBucket(tmpPath, bucket);
748
749 /* We need a temporary directory which we can put the OVF file & all
750 * disk images in */
751 vrc = RTDirCreateTemp(pszTmpDir);
752 if (RT_FAILURE(vrc))
753 throw setError(VBOX_E_FILE_ERROR,
754 tr("Cannot create temporary directory '%s'"), pszTmpDir);
755
756 /* The temporary name of the target OVF file */
757 strTmpOvf = Utf8StrFmt("%s/%s", pszTmpDir, RTPathFilename(tmpPath.c_str()));
758
759 /* Next we have to download the OVF */
760 vrc = RTS3Create(&hS3, pTask->locInfo.strUsername.c_str(), pTask->locInfo.strPassword.c_str(), pTask->locInfo.strHostname.c_str(), "virtualbox-agent/"VBOX_VERSION_STRING);
761 if(RT_FAILURE(vrc))
762 throw setError(VBOX_E_IPRT_ERROR,
763 tr("Cannot create S3 service handler"));
764 RTS3SetProgressCallback(hS3, pTask->updateProgress, &pTask);
765
766 /* Get it */
767 char *pszFilename = RTPathFilename(strTmpOvf.c_str());
768 vrc = RTS3GetKey(hS3, bucket.c_str(), pszFilename, strTmpOvf.c_str());
769 if (RT_FAILURE(vrc))
770 {
771 if(vrc == VERR_S3_CANCELED)
772 throw S_OK; /* todo: !!!!!!!!!!!!! */
773 else if(vrc == VERR_S3_ACCESS_DENIED)
774 throw setError(E_ACCESSDENIED,
775 tr("Cannot download file '%s' from S3 storage server (Access denied). Make sure that your credentials are right. Also check that your host clock is properly synced"), pszFilename);
776 else if(vrc == VERR_S3_NOT_FOUND)
777 throw setError(VBOX_E_FILE_ERROR,
778 tr("Cannot download file '%s' from S3 storage server (File not found)"), pszFilename);
779 else
780 throw setError(VBOX_E_IPRT_ERROR,
781 tr("Cannot download file '%s' from S3 storage server (%Rrc)"), pszFilename, vrc);
782 }
783
784 /* Close the connection early */
785 RTS3Destroy(hS3);
786 hS3 = NIL_RTS3;
787
788 if (!pTask->pProgress.isNull())
789 pTask->pProgress->SetNextOperation(Bstr(tr("Reading")), 1);
790
791 /* Prepare the temporary reading of the OVF */
792 ComObjPtr<Progress> progress;
793 LocationInfo li;
794 li.strPath = strTmpOvf;
795 /* Start the reading from the fs */
796 rc = readImpl(li, progress);
797 if (FAILED(rc)) throw rc;
798
799 /* Unlock the appliance for the reading thread */
800 appLock.release();
801 /* Wait until the reading is done, but report the progress back to the
802 caller */
803 ComPtr<IProgress> progressInt(progress);
804 waitForAsyncProgress(pTask->pProgress, progressInt); /* Any errors will be thrown */
805
806 /* Again lock the appliance for the next steps */
807 appLock.acquire();
808 }
809 catch(HRESULT aRC)
810 {
811 rc = aRC;
812 }
813 /* Cleanup */
814 RTS3Destroy(hS3);
815 /* Delete all files which where temporary created */
816 if (RTPathExists(strTmpOvf.c_str()))
817 {
818 vrc = RTFileDelete(strTmpOvf.c_str());
819 if(RT_FAILURE(vrc))
820 rc = setError(VBOX_E_FILE_ERROR,
821 tr("Cannot delete file '%s' (%Rrc)"), strTmpOvf.c_str(), vrc);
822 }
823 /* Delete the temporary directory */
824 if (RTPathExists(pszTmpDir))
825 {
826 vrc = RTDirRemove(pszTmpDir);
827 if(RT_FAILURE(vrc))
828 rc = setError(VBOX_E_FILE_ERROR,
829 tr("Cannot delete temporary directory '%s' (%Rrc)"), pszTmpDir, vrc);
830 }
831 if (pszTmpDir)
832 RTStrFree(pszTmpDir);
833
834 LogFlowFunc(("rc=%Rhrc\n", rc));
835 LogFlowFuncLeave();
836
837 return VINF_SUCCESS;
838}
839
840/**
841 * Helper that converts VirtualSystem attachment values into VirtualBox attachment values.
842 * Throws HRESULT values on errors!
843 *
844 * @param hdc
845 * @param vd
846 * @param mhda
847 */
848void Appliance::convertDiskAttachmentValues(const HardDiskController &hdc,
849 uint32_t ulAddressOnParent,
850 Bstr &controllerType,
851 int32_t &lChannel,
852 int32_t &lDevice)
853{
854 switch (hdc.system)
855 {
856 case HardDiskController::IDE:
857 // For the IDE bus, the channel parameter can be either 0 or 1, to specify the primary
858 // or secondary IDE controller, respectively. For the primary controller of the IDE bus,
859 // the device number can be either 0 or 1, to specify the master or the slave device,
860 // respectively. For the secondary IDE controller, the device number is always 1 because
861 // the master device is reserved for the CD-ROM drive.
862 controllerType = Bstr("IDE Controller");
863 switch (ulAddressOnParent)
864 {
865 case 0: // interpret this as primary master
866 lChannel = (long)0;
867 lDevice = (long)0;
868 break;
869
870 case 1: // interpret this as primary slave
871 lChannel = (long)0;
872 lDevice = (long)1;
873 break;
874
875 case 2: // interpret this as secondary master
876 lChannel = (long)1;
877 lDevice = (long)0;
878 break;
879
880 case 3: // interpret this as secondary slave
881 lChannel = (long)1;
882 lDevice = (long)1;
883 break;
884
885 default:
886 throw setError(VBOX_E_NOT_SUPPORTED,
887 tr("Invalid channel %RI16 specified; IDE controllers support only 0, 1 or 2"), ulAddressOnParent);
888 break;
889 }
890 break;
891
892 case HardDiskController::SATA:
893 controllerType = Bstr("SATA Controller");
894 lChannel = (long)ulAddressOnParent;
895 lDevice = (long)0;
896 break;
897
898 case HardDiskController::SCSI:
899 controllerType = Bstr("SCSI Controller");
900 lChannel = (long)ulAddressOnParent;
901 lDevice = (long)0;
902 break;
903
904 default: break;
905 }
906}
907
908/**
909 * Implementation for importing OVF data into VirtualBox. This starts a new thread which will call
910 * Appliance::taskThreadImportOVF().
911 *
912 * This is in a separate private method because it is used from two locations:
913 *
914 * 1) from the public Appliance::ImportMachines().
915 * 2) from Appliance::importS3(), which got called from a previous instance of Appliance::taskThreadImportOVF().
916 *
917 * @param aLocInfo
918 * @param aProgress
919 * @return
920 */
921HRESULT Appliance::importImpl(const LocationInfo &aLocInfo, ComObjPtr<Progress> &aProgress)
922{
923 Bstr progressDesc = BstrFmt(tr("Import appliance '%s'"),
924 aLocInfo.strPath.c_str());
925 HRESULT rc = S_OK;
926
927 /* todo: This progress init stuff should be done a little bit more generic */
928 if (aLocInfo.storageType == VFSType_File)
929 rc = setUpProgressFS(aProgress, progressDesc);
930 else
931 rc = setUpProgressImportS3(aProgress, progressDesc);
932 if (FAILED(rc)) throw rc;
933
934 /* Initialize our worker task */
935 std::auto_ptr<TaskOVF> task(new TaskOVF(this, TaskOVF::Import, aLocInfo, aProgress));
936
937 rc = task->startThread();
938 if (FAILED(rc)) throw rc;
939
940 /* Don't destruct on success */
941 task.release();
942
943 return rc;
944}
945
946/**
947 * Actual worker code for importing OVF data into VirtualBox. This is called from Appliance::taskThreadImportOVF()
948 * and therefore runs on the OVF import worker thread. This runs in two contexts:
949 *
950 * 1) in a first worker thread; in that case, Appliance::ImportMachines() called Appliance::importImpl();
951 *
952 * 2) in a second worker thread; in that case, Appliance::ImportMachines() called Appliance::importImpl(), which
953 * called Appliance::importS3(), which called Appliance::importImpl(), which then called this.
954 *
955 * @param pTask
956 * @return
957 */
958HRESULT Appliance::importFS(const LocationInfo &locInfo, ComObjPtr<Progress> &pProgress)
959{
960 LogFlowFuncEnter();
961 LogFlowFunc(("Appliance %p\n", this));
962
963 AutoCaller autoCaller(this);
964 if (FAILED(autoCaller.rc())) return autoCaller.rc();
965
966 AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);
967
968 if (!isApplianceIdle())
969 return E_ACCESSDENIED;
970
971 // Change the appliance state so we can safely leave the lock while doing time-consuming
972 // disk imports; also the below method calls do all kinds of locking which conflicts with
973 // the appliance object lock
974 m->state = Data::ApplianceImporting;
975 appLock.release();
976
977 HRESULT rc = S_OK;
978
979 // rollback for errors:
980 // a list of images that we created/imported
981 list<MyHardDiskAttachment> llHardDiskAttachments;
982 list< ComPtr<IMedium> > llHardDisksCreated;
983 list<Bstr> llMachinesRegistered; // list of string UUIDs
984
985 ComPtr<ISession> session;
986 bool fSessionOpen = false;
987 rc = session.createInprocObject(CLSID_Session);
988 if (FAILED(rc)) return rc;
989
990 const OVFReader &reader = *m->pReader;
991 // this is safe to access because this thread only gets started
992 // if pReader != NULL
993
994 /* If an manifest file exists, verify the content. Therefore we need all
995 * files which are referenced by the OVF & the OVF itself */
996 Utf8Str strMfFile = manifestFileName(locInfo.strPath);
997 list<Utf8Str> filesList;
998 if (RTPathExists(strMfFile.c_str()))
999 {
1000 Utf8Str strSrcDir(locInfo.strPath);
1001 strSrcDir.stripFilename();
1002 /* Add every disks of every virtual system to an internal list */
1003 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;
1004 for (it = m->virtualSystemDescriptions.begin();
1005 it != m->virtualSystemDescriptions.end();
1006 ++it)
1007 {
1008 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);
1009 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);
1010 std::list<VirtualSystemDescriptionEntry*>::const_iterator itH;
1011 for (itH = avsdeHDs.begin();
1012 itH != avsdeHDs.end();
1013 ++itH)
1014 {
1015 VirtualSystemDescriptionEntry *vsdeHD = *itH;
1016 /* Find the disk from the OVF's disk list */
1017 DiskImagesMap::const_iterator itDiskImage = reader.m_mapDisks.find(vsdeHD->strRef);
1018 const DiskImage &di = itDiskImage->second;
1019 Utf8StrFmt strSrcFilePath("%s%c%s", strSrcDir.c_str(), RTPATH_DELIMITER, di.strHref.c_str());
1020 filesList.push_back(strSrcFilePath);
1021 }
1022 }
1023 /* Create the test list */
1024 PRTMANIFESTTEST pTestList = (PRTMANIFESTTEST)RTMemAllocZ(sizeof(RTMANIFESTTEST)*(filesList.size()+1));
1025 pTestList[0].pszTestFile = (char*)locInfo.strPath.c_str();
1026 pTestList[0].pszTestDigest = (char*)m->strOVFSHA1Digest.c_str();
1027 int vrc = VINF_SUCCESS;
1028 size_t i = 1;
1029 list<Utf8Str>::const_iterator it1;
1030 for (it1 = filesList.begin();
1031 it1 != filesList.end();
1032 ++it1, ++i)
1033 {
1034 char* pszDigest;
1035 vrc = RTSha1Digest((*it1).c_str(), &pszDigest);
1036 pTestList[i].pszTestFile = (char*)(*it1).c_str();
1037 pTestList[i].pszTestDigest = pszDigest;
1038 }
1039 size_t cIndexOnError;
1040 vrc = RTManifestVerify(strMfFile.c_str(), pTestList, filesList.size() + 1, &cIndexOnError);
1041 if (vrc == VERR_MANIFEST_DIGEST_MISMATCH)
1042 rc = setError(VBOX_E_FILE_ERROR,
1043 tr("The SHA1 digest of '%s' doesn't match to the one in '%s'"),
1044 RTPathFilename(pTestList[cIndexOnError].pszTestFile),
1045 RTPathFilename(strMfFile.c_str()));
1046 else if (RT_FAILURE(vrc))
1047 rc = setError(VBOX_E_FILE_ERROR,
1048 tr("Couldn't verify the content of '%s' against the available files (%Rrc)"),
1049 RTPathFilename(strMfFile.c_str()),
1050 vrc);
1051 /* Cleanup */
1052 for (size_t j = 1;
1053 j < filesList.size();
1054 ++j)
1055 RTStrFree(pTestList[j].pszTestDigest);
1056 RTMemFree(pTestList);
1057 if (FAILED(rc)) return rc;
1058 }
1059
1060 list<VirtualSystem>::const_iterator it;
1061 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it1;
1062 /* Iterate through all virtual systems of that appliance */
1063 size_t i = 0;
1064 for (it = reader.m_llVirtualSystems.begin(),
1065 it1 = m->virtualSystemDescriptions.begin();
1066 it != reader.m_llVirtualSystems.end();
1067 ++it, ++it1, ++i)
1068 {
1069 const VirtualSystem &vsysThis = *it;
1070 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it1);
1071
1072 ComPtr<IMachine> pNewMachine;
1073
1074 /* Catch possible errors */
1075 try
1076 {
1077 /* Guest OS type */
1078 std::list<VirtualSystemDescriptionEntry*> vsdeOS;
1079 vsdeOS = vsdescThis->findByType(VirtualSystemDescriptionType_OS);
1080 if (vsdeOS.size() < 1)
1081 throw setError(VBOX_E_FILE_ERROR,
1082 tr("Missing guest OS type"));
1083 const Utf8Str &strOsTypeVBox = vsdeOS.front()->strVbox;
1084
1085 /* Now that we know the base system get our internal defaults based on that. */
1086 ComPtr<IGuestOSType> osType;
1087 rc = mVirtualBox->GetGuestOSType(Bstr(strOsTypeVBox), osType.asOutParam());
1088 if (FAILED(rc)) throw rc;
1089
1090 /* Create the machine */
1091 /* First get the name */
1092 std::list<VirtualSystemDescriptionEntry*> vsdeName = vsdescThis->findByType(VirtualSystemDescriptionType_Name);
1093 if (vsdeName.size() < 1)
1094 throw setError(VBOX_E_FILE_ERROR,
1095 tr("Missing VM name"));
1096 const Utf8Str &strNameVBox = vsdeName.front()->strVbox;
1097 rc = mVirtualBox->CreateMachine(Bstr(strNameVBox), Bstr(strOsTypeVBox),
1098 Bstr(), Bstr(), FALSE,
1099 pNewMachine.asOutParam());
1100 if (FAILED(rc)) throw rc;
1101
1102 // and the description
1103 std::list<VirtualSystemDescriptionEntry*> vsdeDescription = vsdescThis->findByType(VirtualSystemDescriptionType_Description);
1104 if (vsdeDescription.size())
1105 {
1106 const Utf8Str &strDescription = vsdeDescription.front()->strVbox;
1107 rc = pNewMachine->COMSETTER(Description)(Bstr(strDescription));
1108 if (FAILED(rc)) throw rc;
1109 }
1110
1111 /* CPU count */
1112 std::list<VirtualSystemDescriptionEntry*> vsdeCPU = vsdescThis->findByType(VirtualSystemDescriptionType_CPU);
1113 ComAssertMsgThrow(vsdeCPU.size() == 1, ("CPU count missing"), E_FAIL);
1114 const Utf8Str &cpuVBox = vsdeCPU.front()->strVbox;
1115 ULONG tmpCount = (ULONG)RTStrToUInt64(cpuVBox.c_str());
1116 rc = pNewMachine->COMSETTER(CPUCount)(tmpCount);
1117 if (FAILED(rc)) throw rc;
1118 bool fEnableIOApic = false;
1119 /* We need HWVirt & IO-APIC if more than one CPU is requested */
1120 if (tmpCount > 1)
1121 {
1122 rc = pNewMachine->SetHWVirtExProperty(HWVirtExPropertyType_Enabled, TRUE);
1123 if (FAILED(rc)) throw rc;
1124
1125 fEnableIOApic = true;
1126 }
1127
1128 /* RAM */
1129 std::list<VirtualSystemDescriptionEntry*> vsdeRAM = vsdescThis->findByType(VirtualSystemDescriptionType_Memory);
1130 ComAssertMsgThrow(vsdeRAM.size() == 1, ("RAM size missing"), E_FAIL);
1131 const Utf8Str &memoryVBox = vsdeRAM.front()->strVbox;
1132 ULONG tt = (ULONG)RTStrToUInt64(memoryVBox.c_str());
1133 rc = pNewMachine->COMSETTER(MemorySize)(tt);
1134 if (FAILED(rc)) throw rc;
1135
1136 /* VRAM */
1137 /* Get the recommended VRAM for this guest OS type */
1138 ULONG vramVBox;
1139 rc = osType->COMGETTER(RecommendedVRAM)(&vramVBox);
1140 if (FAILED(rc)) throw rc;
1141
1142 /* Set the VRAM */
1143 rc = pNewMachine->COMSETTER(VRAMSize)(vramVBox);
1144 if (FAILED(rc)) throw rc;
1145
1146 /* I/O APIC: so far we have no setting for this. Enable it if we
1147 import a Windows VM because if if Windows was installed without IOAPIC,
1148 it will not mind finding an one later on, but if Windows was installed
1149 _with_ an IOAPIC, it will bluescreen if it's not found */
1150 Bstr bstrFamilyId;
1151 rc = osType->COMGETTER(FamilyId)(bstrFamilyId.asOutParam());
1152 if (FAILED(rc)) throw rc;
1153
1154 Utf8Str strFamilyId(bstrFamilyId);
1155 if (strFamilyId == "Windows")
1156 fEnableIOApic = true;
1157
1158 /* If IP-APIC should be enabled could be have different reasons.
1159 See CPU count & the Win test above. Here we enable it if it was
1160 previously requested. */
1161 if (fEnableIOApic)
1162 {
1163 ComPtr<IBIOSSettings> pBIOSSettings;
1164 rc = pNewMachine->COMGETTER(BIOSSettings)(pBIOSSettings.asOutParam());
1165 if (FAILED(rc)) throw rc;
1166
1167 rc = pBIOSSettings->COMSETTER(IOAPICEnabled)(TRUE);
1168 if (FAILED(rc)) throw rc;
1169 }
1170
1171 /* Audio Adapter */
1172 std::list<VirtualSystemDescriptionEntry*> vsdeAudioAdapter = vsdescThis->findByType(VirtualSystemDescriptionType_SoundCard);
1173 /* @todo: we support one audio adapter only */
1174 if (vsdeAudioAdapter.size() > 0)
1175 {
1176 const Utf8Str& audioAdapterVBox = vsdeAudioAdapter.front()->strVbox;
1177 if (audioAdapterVBox.compare("null", Utf8Str::CaseInsensitive) != 0)
1178 {
1179 uint32_t audio = RTStrToUInt32(audioAdapterVBox.c_str());
1180 ComPtr<IAudioAdapter> audioAdapter;
1181 rc = pNewMachine->COMGETTER(AudioAdapter)(audioAdapter.asOutParam());
1182 if (FAILED(rc)) throw rc;
1183 rc = audioAdapter->COMSETTER(Enabled)(true);
1184 if (FAILED(rc)) throw rc;
1185 rc = audioAdapter->COMSETTER(AudioController)(static_cast<AudioControllerType_T>(audio));
1186 if (FAILED(rc)) throw rc;
1187 }
1188 }
1189
1190#ifdef VBOX_WITH_USB
1191 /* USB Controller */
1192 std::list<VirtualSystemDescriptionEntry*> vsdeUSBController = vsdescThis->findByType(VirtualSystemDescriptionType_USBController);
1193 // USB support is enabled if there's at least one such entry; to disable USB support,
1194 // the type of the USB item would have been changed to "ignore"
1195 bool fUSBEnabled = vsdeUSBController.size() > 0;
1196
1197 ComPtr<IUSBController> usbController;
1198 rc = pNewMachine->COMGETTER(USBController)(usbController.asOutParam());
1199 if (FAILED(rc)) throw rc;
1200 rc = usbController->COMSETTER(Enabled)(fUSBEnabled);
1201 if (FAILED(rc)) throw rc;
1202#endif /* VBOX_WITH_USB */
1203
1204 /* Change the network adapters */
1205 std::list<VirtualSystemDescriptionEntry*> vsdeNW = vsdescThis->findByType(VirtualSystemDescriptionType_NetworkAdapter);
1206 if (vsdeNW.size() == 0)
1207 {
1208 /* No network adapters, so we have to disable our default one */
1209 ComPtr<INetworkAdapter> nwVBox;
1210 rc = pNewMachine->GetNetworkAdapter(0, nwVBox.asOutParam());
1211 if (FAILED(rc)) throw rc;
1212 rc = nwVBox->COMSETTER(Enabled)(false);
1213 if (FAILED(rc)) throw rc;
1214 }
1215 else
1216 {
1217 list<VirtualSystemDescriptionEntry*>::const_iterator nwIt;
1218 /* Iterate through all network cards. We support 8 network adapters
1219 * at the maximum. (@todo: warn if there are more!) */
1220 size_t a = 0;
1221 for (nwIt = vsdeNW.begin();
1222 (nwIt != vsdeNW.end() && a < SchemaDefs::NetworkAdapterCount);
1223 ++nwIt, ++a)
1224 {
1225 const VirtualSystemDescriptionEntry* pvsys = *nwIt;
1226
1227 const Utf8Str &nwTypeVBox = pvsys->strVbox;
1228 uint32_t tt1 = RTStrToUInt32(nwTypeVBox.c_str());
1229 ComPtr<INetworkAdapter> pNetworkAdapter;
1230 rc = pNewMachine->GetNetworkAdapter((ULONG)a, pNetworkAdapter.asOutParam());
1231 if (FAILED(rc)) throw rc;
1232 /* Enable the network card & set the adapter type */
1233 rc = pNetworkAdapter->COMSETTER(Enabled)(true);
1234 if (FAILED(rc)) throw rc;
1235 rc = pNetworkAdapter->COMSETTER(AdapterType)(static_cast<NetworkAdapterType_T>(tt1));
1236 if (FAILED(rc)) throw rc;
1237
1238 // default is NAT; change to "bridged" if extra conf says so
1239 if (!pvsys->strExtraConfig.compare("type=Bridged", Utf8Str::CaseInsensitive))
1240 {
1241 /* Attach to the right interface */
1242 rc = pNetworkAdapter->AttachToBridgedInterface();
1243 if (FAILED(rc)) throw rc;
1244 ComPtr<IHost> host;
1245 rc = mVirtualBox->COMGETTER(Host)(host.asOutParam());
1246 if (FAILED(rc)) throw rc;
1247 com::SafeIfaceArray<IHostNetworkInterface> nwInterfaces;
1248 rc = host->COMGETTER(NetworkInterfaces)(ComSafeArrayAsOutParam(nwInterfaces));
1249 if (FAILED(rc)) throw rc;
1250 /* We search for the first host network interface which
1251 * is usable for bridged networking */
1252 for (size_t j = 0;
1253 j < nwInterfaces.size();
1254 ++j)
1255 {
1256 HostNetworkInterfaceType_T itype;
1257 rc = nwInterfaces[j]->COMGETTER(InterfaceType)(&itype);
1258 if (FAILED(rc)) throw rc;
1259 if (itype == HostNetworkInterfaceType_Bridged)
1260 {
1261 Bstr name;
1262 rc = nwInterfaces[j]->COMGETTER(Name)(name.asOutParam());
1263 if (FAILED(rc)) throw rc;
1264 /* Set the interface name to attach to */
1265 pNetworkAdapter->COMSETTER(HostInterface)(name);
1266 if (FAILED(rc)) throw rc;
1267 break;
1268 }
1269 }
1270 }
1271 /* Next test for host only interfaces */
1272 else if (!pvsys->strExtraConfig.compare("type=HostOnly", Utf8Str::CaseInsensitive))
1273 {
1274 /* Attach to the right interface */
1275 rc = pNetworkAdapter->AttachToHostOnlyInterface();
1276 if (FAILED(rc)) throw rc;
1277 ComPtr<IHost> host;
1278 rc = mVirtualBox->COMGETTER(Host)(host.asOutParam());
1279 if (FAILED(rc)) throw rc;
1280 com::SafeIfaceArray<IHostNetworkInterface> nwInterfaces;
1281 rc = host->COMGETTER(NetworkInterfaces)(ComSafeArrayAsOutParam(nwInterfaces));
1282 if (FAILED(rc)) throw rc;
1283 /* We search for the first host network interface which
1284 * is usable for host only networking */
1285 for (size_t j = 0;
1286 j < nwInterfaces.size();
1287 ++j)
1288 {
1289 HostNetworkInterfaceType_T itype;
1290 rc = nwInterfaces[j]->COMGETTER(InterfaceType)(&itype);
1291 if (FAILED(rc)) throw rc;
1292 if (itype == HostNetworkInterfaceType_HostOnly)
1293 {
1294 Bstr name;
1295 rc = nwInterfaces[j]->COMGETTER(Name)(name.asOutParam());
1296 if (FAILED(rc)) throw rc;
1297 /* Set the interface name to attach to */
1298 pNetworkAdapter->COMSETTER(HostInterface)(name);
1299 if (FAILED(rc)) throw rc;
1300 break;
1301 }
1302 }
1303 }
1304 }
1305 }
1306
1307 /* Hard disk controller IDE */
1308 std::list<VirtualSystemDescriptionEntry*> vsdeHDCIDE = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerIDE);
1309 if (vsdeHDCIDE.size() > 1)
1310 throw setError(VBOX_E_FILE_ERROR,
1311 tr("Too many IDE controllers in OVF; import facility only supports one"));
1312 if (vsdeHDCIDE.size() == 1)
1313 {
1314 ComPtr<IStorageController> pController;
1315 rc = pNewMachine->AddStorageController(Bstr("IDE Controller"), StorageBus_IDE, pController.asOutParam());
1316 if (FAILED(rc)) throw rc;
1317
1318 const char *pcszIDEType = vsdeHDCIDE.front()->strVbox.c_str();
1319 if (!strcmp(pcszIDEType, "PIIX3"))
1320 rc = pController->COMSETTER(ControllerType)(StorageControllerType_PIIX3);
1321 else if (!strcmp(pcszIDEType, "PIIX4"))
1322 rc = pController->COMSETTER(ControllerType)(StorageControllerType_PIIX4);
1323 else if (!strcmp(pcszIDEType, "ICH6"))
1324 rc = pController->COMSETTER(ControllerType)(StorageControllerType_ICH6);
1325 else
1326 throw setError(VBOX_E_FILE_ERROR,
1327 tr("Invalid IDE controller type \"%s\""),
1328 pcszIDEType);
1329 if (FAILED(rc)) throw rc;
1330 }
1331#ifdef VBOX_WITH_AHCI
1332 /* Hard disk controller SATA */
1333 std::list<VirtualSystemDescriptionEntry*> vsdeHDCSATA = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerSATA);
1334 if (vsdeHDCSATA.size() > 1)
1335 throw setError(VBOX_E_FILE_ERROR,
1336 tr("Too many SATA controllers in OVF; import facility only supports one"));
1337 if (vsdeHDCSATA.size() > 0)
1338 {
1339 ComPtr<IStorageController> pController;
1340 const Utf8Str &hdcVBox = vsdeHDCSATA.front()->strVbox;
1341 if (hdcVBox == "AHCI")
1342 {
1343 rc = pNewMachine->AddStorageController(Bstr("SATA Controller"), StorageBus_SATA, pController.asOutParam());
1344 if (FAILED(rc)) throw rc;
1345 }
1346 else
1347 throw setError(VBOX_E_FILE_ERROR,
1348 tr("Invalid SATA controller type \"%s\""),
1349 hdcVBox.c_str());
1350 }
1351#endif /* VBOX_WITH_AHCI */
1352
1353#ifdef VBOX_WITH_LSILOGIC
1354 /* Hard disk controller SCSI */
1355 std::list<VirtualSystemDescriptionEntry*> vsdeHDCSCSI = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerSCSI);
1356 if (vsdeHDCSCSI.size() > 1)
1357 throw setError(VBOX_E_FILE_ERROR,
1358 tr("Too many SCSI controllers in OVF; import facility only supports one"));
1359 if (vsdeHDCSCSI.size() > 0)
1360 {
1361 ComPtr<IStorageController> pController;
1362 StorageControllerType_T controllerType;
1363 const Utf8Str &hdcVBox = vsdeHDCSCSI.front()->strVbox;
1364 if (hdcVBox == "LsiLogic")
1365 controllerType = StorageControllerType_LsiLogic;
1366 else if (hdcVBox == "BusLogic")
1367 controllerType = StorageControllerType_BusLogic;
1368 else
1369 throw setError(VBOX_E_FILE_ERROR,
1370 tr("Invalid SCSI controller type \"%s\""),
1371 hdcVBox.c_str());
1372
1373 rc = pNewMachine->AddStorageController(Bstr("SCSI Controller"), StorageBus_SCSI, pController.asOutParam());
1374 if (FAILED(rc)) throw rc;
1375 rc = pController->COMSETTER(ControllerType)(controllerType);
1376 if (FAILED(rc)) throw rc;
1377 }
1378#endif /* VBOX_WITH_LSILOGIC */
1379
1380 /* Now its time to register the machine before we add any hard disks */
1381 rc = mVirtualBox->RegisterMachine(pNewMachine);
1382 if (FAILED(rc)) throw rc;
1383
1384 Bstr bstrNewMachineId;
1385 rc = pNewMachine->COMGETTER(Id)(bstrNewMachineId.asOutParam());
1386 if (FAILED(rc)) throw rc;
1387
1388 // store new machine for roll-back in case of errors
1389 llMachinesRegistered.push_back(bstrNewMachineId);
1390
1391 // Add floppies and CD-ROMs to the appropriate controllers.
1392 std::list<VirtualSystemDescriptionEntry*> vsdeFloppy = vsdescThis->findByType(VirtualSystemDescriptionType_Floppy);
1393 if (vsdeFloppy.size() > 1)
1394 throw setError(VBOX_E_FILE_ERROR,
1395 tr("Too many floppy controllers in OVF; import facility only supports one"));
1396 std::list<VirtualSystemDescriptionEntry*> vsdeCDROM = vsdescThis->findByType(VirtualSystemDescriptionType_CDROM);
1397 if ( (vsdeFloppy.size() > 0)
1398 || (vsdeCDROM.size() > 0)
1399 )
1400 {
1401 // If there's an error here we need to close the session, so
1402 // we need another try/catch block.
1403
1404 try
1405 {
1406 /* In order to attach things we need to open a session
1407 * for the new machine */
1408 rc = mVirtualBox->OpenSession(session, bstrNewMachineId);
1409 if (FAILED(rc)) throw rc;
1410 fSessionOpen = true;
1411
1412 ComPtr<IMachine> sMachine;
1413 rc = session->COMGETTER(Machine)(sMachine.asOutParam());
1414 if (FAILED(rc)) throw rc;
1415
1416 // floppy first
1417 if (vsdeFloppy.size() == 1)
1418 {
1419 ComPtr<IStorageController> pController;
1420 rc = sMachine->AddStorageController(Bstr("Floppy Controller"), StorageBus_Floppy, pController.asOutParam());
1421 if (FAILED(rc)) throw rc;
1422
1423 Bstr bstrName;
1424 rc = pController->COMGETTER(Name)(bstrName.asOutParam());
1425 if (FAILED(rc)) throw rc;
1426
1427 // this is for rollback later
1428 MyHardDiskAttachment mhda;
1429 mhda.bstrUuid = bstrNewMachineId;
1430 mhda.pMachine = pNewMachine;
1431 mhda.controllerType = bstrName;
1432 mhda.lChannel = 0;
1433 mhda.lDevice = 0;
1434
1435 Log(("Attaching floppy\n"));
1436
1437 rc = sMachine->AttachDevice(mhda.controllerType,
1438 mhda.lChannel,
1439 mhda.lDevice,
1440 DeviceType_Floppy,
1441 NULL);
1442 if (FAILED(rc)) throw rc;
1443
1444 llHardDiskAttachments.push_back(mhda);
1445 }
1446
1447
1448 // CD-ROMs next
1449 for (std::list<VirtualSystemDescriptionEntry*>::const_iterator jt = vsdeCDROM.begin();
1450 jt != vsdeCDROM.end();
1451 ++jt)
1452 {
1453 // for now always attach to secondary master on IDE controller;
1454 // there seems to be no useful information in OVF where else to
1455 // attach jt (@todo test with latest versions of OVF software)
1456
1457 // find the IDE controller
1458 const HardDiskController *pController = NULL;
1459 for (ControllersMap::const_iterator kt = vsysThis.mapControllers.begin();
1460 kt != vsysThis.mapControllers.end();
1461 ++kt)
1462 {
1463 if (kt->second.system == HardDiskController::IDE)
1464 {
1465 pController = &kt->second;
1466 }
1467 }
1468
1469 if (!pController)
1470 throw setError(VBOX_E_FILE_ERROR,
1471 tr("OVF wants a CD-ROM drive but cannot find IDE controller, which is required in this version of VirtualBox"));
1472
1473 // this is for rollback later
1474 MyHardDiskAttachment mhda;
1475 mhda.bstrUuid = bstrNewMachineId;
1476 mhda.pMachine = pNewMachine;
1477
1478 convertDiskAttachmentValues(*pController,
1479 2, // interpreted as secondary master
1480 mhda.controllerType, // Bstr
1481 mhda.lChannel,
1482 mhda.lDevice);
1483
1484 Log(("Attaching CD-ROM to channel %d on device %d\n", mhda.lChannel, mhda.lDevice));
1485
1486 rc = sMachine->AttachDevice(mhda.controllerType,
1487 mhda.lChannel,
1488 mhda.lDevice,
1489 DeviceType_DVD,
1490 NULL);
1491 if (FAILED(rc)) throw rc;
1492
1493 llHardDiskAttachments.push_back(mhda);
1494 } // end for (itHD = avsdeHDs.begin();
1495
1496 rc = sMachine->SaveSettings();
1497 if (FAILED(rc)) throw rc;
1498
1499 // only now that we're done with all disks, close the session
1500 rc = session->Close();
1501 if (FAILED(rc)) throw rc;
1502 fSessionOpen = false;
1503 }
1504 catch(HRESULT /* aRC */)
1505 {
1506 if (fSessionOpen)
1507 session->Close();
1508
1509 throw;
1510 }
1511 }
1512
1513 /* Create the hard disks & connect them to the appropriate controllers. */
1514 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);
1515 if (avsdeHDs.size() > 0)
1516 {
1517 // If there's an error here we need to close the session, so
1518 // we need another try/catch block.
1519 ComPtr<IMedium> srcHdVBox;
1520 bool fSourceHdNeedsClosing = false;
1521
1522 try
1523 {
1524 /* In order to attach hard disks we need to open a session
1525 * for the new machine */
1526 rc = mVirtualBox->OpenSession(session, bstrNewMachineId);
1527 if (FAILED(rc)) throw rc;
1528 fSessionOpen = true;
1529
1530 /* The disk image has to be on the same place as the OVF file. So
1531 * strip the filename out of the full file path. */
1532 Utf8Str strSrcDir(locInfo.strPath);
1533 strSrcDir.stripFilename();
1534
1535 /* Iterate over all given disk images */
1536 list<VirtualSystemDescriptionEntry*>::const_iterator itHD;
1537 for (itHD = avsdeHDs.begin();
1538 itHD != avsdeHDs.end();
1539 ++itHD)
1540 {
1541 VirtualSystemDescriptionEntry *vsdeHD = *itHD;
1542
1543 /* Check if the destination file exists already or the
1544 * destination path is empty. */
1545 if ( vsdeHD->strVbox.isEmpty()
1546 || RTPathExists(vsdeHD->strVbox.c_str())
1547 )
1548 /* This isn't allowed */
1549 throw setError(VBOX_E_FILE_ERROR,
1550 tr("Destination file '%s' exists",
1551 vsdeHD->strVbox.c_str()));
1552
1553 /* Find the disk from the OVF's disk list */
1554 DiskImagesMap::const_iterator itDiskImage = reader.m_mapDisks.find(vsdeHD->strRef);
1555 /* vsdeHD->strRef contains the disk identifier (e.g. "vmdisk1"), which should exist
1556 in the virtual system's disks map under that ID and also in the global images map. */
1557 VirtualDisksMap::const_iterator itVirtualDisk = vsysThis.mapVirtualDisks.find(vsdeHD->strRef);
1558
1559 if ( itDiskImage == reader.m_mapDisks.end()
1560 || itVirtualDisk == vsysThis.mapVirtualDisks.end()
1561 )
1562 throw setError(E_FAIL,
1563 tr("Internal inconsistency looking up disk images."));
1564
1565 const DiskImage &di = itDiskImage->second;
1566 const VirtualDisk &vd = itVirtualDisk->second;
1567
1568 /* Make sure all target directories exists */
1569 rc = VirtualBox::ensureFilePathExists(vsdeHD->strVbox.c_str());
1570 if (FAILED(rc))
1571 throw rc;
1572
1573 // subprogress object for hard disk
1574 ComPtr<IProgress> pProgress2;
1575
1576 ComPtr<IMedium> dstHdVBox;
1577 /* If strHref is empty we have to create a new file */
1578 if (di.strHref.isEmpty())
1579 {
1580 /* Which format to use? */
1581 Bstr srcFormat = L"VDI";
1582 if ( di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#sparse", Utf8Str::CaseInsensitive)
1583 || di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#compressed", Utf8Str::CaseInsensitive))
1584 srcFormat = L"VMDK";
1585 /* Create an empty hard disk */
1586 rc = mVirtualBox->CreateHardDisk(srcFormat, Bstr(vsdeHD->strVbox), dstHdVBox.asOutParam());
1587 if (FAILED(rc)) throw rc;
1588
1589 /* Create a dynamic growing disk image with the given capacity */
1590 rc = dstHdVBox->CreateBaseStorage(di.iCapacity / _1M, MediumVariant_Standard, pProgress2.asOutParam());
1591 if (FAILED(rc)) throw rc;
1592
1593 /* Advance to the next operation */
1594 if (!pProgress.isNull())
1595 pProgress->SetNextOperation(BstrFmt(tr("Creating virtual disk image '%s'"), vsdeHD->strVbox.c_str()),
1596 vsdeHD->ulSizeMB); // operation's weight, as set up with the IProgress originally
1597 }
1598 else
1599 {
1600 /* Construct the source file path */
1601 Utf8StrFmt strSrcFilePath("%s%c%s", strSrcDir.c_str(), RTPATH_DELIMITER, di.strHref.c_str());
1602 /* Check if the source file exists */
1603 if (!RTPathExists(strSrcFilePath.c_str()))
1604 /* This isn't allowed */
1605 throw setError(VBOX_E_FILE_ERROR,
1606 tr("Source virtual disk image file '%s' doesn't exist"),
1607 strSrcFilePath.c_str());
1608
1609 /* Clone the disk image (this is necessary cause the id has
1610 * to be recreated for the case the same hard disk is
1611 * attached already from a previous import) */
1612
1613 /* First open the existing disk image */
1614 rc = mVirtualBox->OpenHardDisk(Bstr(strSrcFilePath),
1615 AccessMode_ReadOnly,
1616 false,
1617 NULL,
1618 false,
1619 NULL,
1620 srcHdVBox.asOutParam());
1621 if (FAILED(rc)) throw rc;
1622 fSourceHdNeedsClosing = true;
1623
1624 /* We need the format description of the source disk image */
1625 Bstr srcFormat;
1626 rc = srcHdVBox->COMGETTER(Format)(srcFormat.asOutParam());
1627 if (FAILED(rc)) throw rc;
1628 /* Create a new hard disk interface for the destination disk image */
1629 rc = mVirtualBox->CreateHardDisk(srcFormat, Bstr(vsdeHD->strVbox), dstHdVBox.asOutParam());
1630 if (FAILED(rc)) throw rc;
1631 /* Clone the source disk image */
1632 rc = srcHdVBox->CloneTo(dstHdVBox, MediumVariant_Standard, NULL, pProgress2.asOutParam());
1633 if (FAILED(rc)) throw rc;
1634
1635 /* Advance to the next operation */
1636 if (!pProgress.isNull())
1637 pProgress->SetNextOperation(BstrFmt(tr("Importing virtual disk image '%s'"), strSrcFilePath.c_str()),
1638 vsdeHD->ulSizeMB); // operation's weight, as set up with the IProgress originally);
1639 }
1640
1641 // now wait for the background disk operation to complete; this throws HRESULTs on error
1642 waitForAsyncProgress(pProgress, pProgress2);
1643
1644 if (fSourceHdNeedsClosing)
1645 {
1646 rc = srcHdVBox->Close();
1647 if (FAILED(rc)) throw rc;
1648 fSourceHdNeedsClosing = false;
1649 }
1650
1651 llHardDisksCreated.push_back(dstHdVBox);
1652 /* Now use the new uuid to attach the disk image to our new machine */
1653 ComPtr<IMachine> sMachine;
1654 rc = session->COMGETTER(Machine)(sMachine.asOutParam());
1655 if (FAILED(rc)) throw rc;
1656 Bstr hdId;
1657 rc = dstHdVBox->COMGETTER(Id)(hdId.asOutParam());
1658 if (FAILED(rc)) throw rc;
1659
1660 /* For now we assume we have one controller of every type only */
1661 HardDiskController hdc = (*vsysThis.mapControllers.find(vd.idController)).second;
1662
1663 // this is for rollback later
1664 MyHardDiskAttachment mhda;
1665 mhda.bstrUuid = bstrNewMachineId;
1666 mhda.pMachine = pNewMachine;
1667
1668 convertDiskAttachmentValues(hdc,
1669 vd.ulAddressOnParent,
1670 mhda.controllerType, // Bstr
1671 mhda.lChannel,
1672 mhda.lDevice);
1673
1674 Log(("Attaching disk %s to channel %d on device %d\n", vsdeHD->strVbox.c_str(), mhda.lChannel, mhda.lDevice));
1675
1676 rc = sMachine->AttachDevice(mhda.controllerType,
1677 mhda.lChannel,
1678 mhda.lDevice,
1679 DeviceType_HardDisk,
1680 hdId);
1681 if (FAILED(rc)) throw rc;
1682
1683 llHardDiskAttachments.push_back(mhda);
1684
1685 rc = sMachine->SaveSettings();
1686 if (FAILED(rc)) throw rc;
1687 } // end for (itHD = avsdeHDs.begin();
1688
1689 // only now that we're done with all disks, close the session
1690 rc = session->Close();
1691 if (FAILED(rc)) throw rc;
1692 fSessionOpen = false;
1693 }
1694 catch(HRESULT /* aRC */)
1695 {
1696 if (fSourceHdNeedsClosing)
1697 srcHdVBox->Close();
1698
1699 if (fSessionOpen)
1700 session->Close();
1701
1702 throw;
1703 }
1704 }
1705 }
1706 catch(HRESULT aRC)
1707 {
1708 rc = aRC;
1709 }
1710
1711 if (FAILED(rc))
1712 break;
1713
1714 } // for (it = pAppliance->m->llVirtualSystems.begin(),
1715
1716 if (FAILED(rc))
1717 {
1718 // with _whatever_ error we've had, do a complete roll-back of
1719 // machines and disks we've created; unfortunately this is
1720 // not so trivially done...
1721
1722 HRESULT rc2;
1723 // detach all hard disks from all machines we created
1724 list<MyHardDiskAttachment>::iterator itM;
1725 for (itM = llHardDiskAttachments.begin();
1726 itM != llHardDiskAttachments.end();
1727 ++itM)
1728 {
1729 const MyHardDiskAttachment &mhda = *itM;
1730 Bstr bstrUuid(mhda.bstrUuid); // make a copy, Windows can't handle const Bstr
1731 rc2 = mVirtualBox->OpenSession(session, bstrUuid);
1732 if (SUCCEEDED(rc2))
1733 {
1734 ComPtr<IMachine> sMachine;
1735 rc2 = session->COMGETTER(Machine)(sMachine.asOutParam());
1736 if (SUCCEEDED(rc2))
1737 {
1738 rc2 = sMachine->DetachDevice(Bstr(mhda.controllerType), mhda.lChannel, mhda.lDevice);
1739 rc2 = sMachine->SaveSettings();
1740 }
1741 session->Close();
1742 }
1743 }
1744
1745 // now clean up all hard disks we created
1746 list< ComPtr<IMedium> >::iterator itHD;
1747 for (itHD = llHardDisksCreated.begin();
1748 itHD != llHardDisksCreated.end();
1749 ++itHD)
1750 {
1751 ComPtr<IMedium> pDisk = *itHD;
1752 ComPtr<IProgress> pProgress2;
1753 rc2 = pDisk->DeleteStorage(pProgress2.asOutParam());
1754 rc2 = pProgress2->WaitForCompletion(-1);
1755 }
1756
1757 // finally, deregister and remove all machines
1758 list<Bstr>::iterator itID;
1759 for (itID = llMachinesRegistered.begin();
1760 itID != llMachinesRegistered.end();
1761 ++itID)
1762 {
1763 Bstr bstrGuid = *itID; // make a copy, Windows can't handle const Bstr
1764 ComPtr<IMachine> failedMachine;
1765 rc2 = mVirtualBox->UnregisterMachine(bstrGuid, failedMachine.asOutParam());
1766 if (SUCCEEDED(rc2))
1767 rc2 = failedMachine->DeleteSettings();
1768 }
1769 }
1770
1771 // restore the appliance state
1772 appLock.acquire();
1773 m->state = Data::ApplianceIdle;
1774
1775 LogFlowFunc(("rc=%Rhrc\n", rc));
1776 LogFlowFuncLeave();
1777
1778 return rc;
1779}
1780
1781/**
1782 * Gets called from taskThreadImportOVF().
1783 * @param pTask
1784 * @return
1785 */
1786HRESULT Appliance::importS3(TaskOVF *pTask)
1787{
1788 LogFlowFuncEnter();
1789 LogFlowFunc(("Appliance %p\n", this));
1790
1791 AutoCaller autoCaller(this);
1792 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1793
1794 AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);
1795
1796 int vrc = VINF_SUCCESS;
1797 RTS3 hS3 = NIL_RTS3;
1798 char szOSTmpDir[RTPATH_MAX];
1799 RTPathTemp(szOSTmpDir, sizeof(szOSTmpDir));
1800 /* The template for the temporary directory created below */
1801 char *pszTmpDir;
1802 RTStrAPrintf(&pszTmpDir, "%s"RTPATH_SLASH_STR"vbox-ovf-XXXXXX", szOSTmpDir);
1803 list< pair<Utf8Str, ULONG> > filesList;
1804
1805 HRESULT rc = S_OK;
1806 try
1807 {
1808 /* Extract the bucket */
1809 Utf8Str tmpPath = pTask->locInfo.strPath;
1810 Utf8Str bucket;
1811 parseBucket(tmpPath, bucket);
1812
1813 /* We need a temporary directory which we can put the all disk images
1814 * in */
1815 vrc = RTDirCreateTemp(pszTmpDir);
1816 if (RT_FAILURE(vrc))
1817 throw setError(VBOX_E_FILE_ERROR,
1818 tr("Cannot create temporary directory '%s'"), pszTmpDir);
1819
1820 /* Add every disks of every virtual system to an internal list */
1821 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;
1822 for (it = m->virtualSystemDescriptions.begin();
1823 it != m->virtualSystemDescriptions.end();
1824 ++it)
1825 {
1826 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);
1827 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);
1828 std::list<VirtualSystemDescriptionEntry*>::const_iterator itH;
1829 for (itH = avsdeHDs.begin();
1830 itH != avsdeHDs.end();
1831 ++itH)
1832 {
1833 const Utf8Str &strTargetFile = (*itH)->strOvf;
1834 if (!strTargetFile.isEmpty())
1835 {
1836 /* The temporary name of the target disk file */
1837 Utf8StrFmt strTmpDisk("%s/%s", pszTmpDir, RTPathFilename(strTargetFile.c_str()));
1838 filesList.push_back(pair<Utf8Str, ULONG>(strTmpDisk, (*itH)->ulSizeMB));
1839 }
1840 }
1841 }
1842
1843 /* Next we have to download the disk images */
1844 vrc = RTS3Create(&hS3, pTask->locInfo.strUsername.c_str(), pTask->locInfo.strPassword.c_str(), pTask->locInfo.strHostname.c_str(), "virtualbox-agent/"VBOX_VERSION_STRING);
1845 if(RT_FAILURE(vrc))
1846 throw setError(VBOX_E_IPRT_ERROR,
1847 tr("Cannot create S3 service handler"));
1848 RTS3SetProgressCallback(hS3, pTask->updateProgress, &pTask);
1849
1850 /* Download all files */
1851 for (list< pair<Utf8Str, ULONG> >::const_iterator it1 = filesList.begin(); it1 != filesList.end(); ++it1)
1852 {
1853 const pair<Utf8Str, ULONG> &s = (*it1);
1854 const Utf8Str &strSrcFile = s.first;
1855 /* Construct the source file name */
1856 char *pszFilename = RTPathFilename(strSrcFile.c_str());
1857 /* Advance to the next operation */
1858 if (!pTask->pProgress.isNull())
1859 pTask->pProgress->SetNextOperation(BstrFmt(tr("Downloading file '%s'"), pszFilename), s.second);
1860
1861 vrc = RTS3GetKey(hS3, bucket.c_str(), pszFilename, strSrcFile.c_str());
1862 if (RT_FAILURE(vrc))
1863 {
1864 if(vrc == VERR_S3_CANCELED)
1865 throw S_OK; /* todo: !!!!!!!!!!!!! */
1866 else if(vrc == VERR_S3_ACCESS_DENIED)
1867 throw setError(E_ACCESSDENIED,
1868 tr("Cannot download file '%s' from S3 storage server (Access denied). Make sure that your credentials are right. Also check that your host clock is properly synced"), pszFilename);
1869 else if(vrc == VERR_S3_NOT_FOUND)
1870 throw setError(VBOX_E_FILE_ERROR,
1871 tr("Cannot download file '%s' from S3 storage server (File not found)"), pszFilename);
1872 else
1873 throw setError(VBOX_E_IPRT_ERROR,
1874 tr("Cannot download file '%s' from S3 storage server (%Rrc)"), pszFilename, vrc);
1875 }
1876 }
1877
1878 /* Provide a OVF file (haven't to exist) so the import routine can
1879 * figure out where the disk images/manifest file are located. */
1880 Utf8StrFmt strTmpOvf("%s/%s", pszTmpDir, RTPathFilename(tmpPath.c_str()));
1881 /* Now check if there is an manifest file. This is optional. */
1882 Utf8Str strManifestFile = manifestFileName(strTmpOvf);
1883 char *pszFilename = RTPathFilename(strManifestFile.c_str());
1884 if (!pTask->pProgress.isNull())
1885 pTask->pProgress->SetNextOperation(BstrFmt(tr("Downloading file '%s'"), pszFilename), 1);
1886
1887 /* Try to download it. If the error is VERR_S3_NOT_FOUND, it isn't fatal. */
1888 vrc = RTS3GetKey(hS3, bucket.c_str(), pszFilename, strManifestFile.c_str());
1889 if (RT_SUCCESS(vrc))
1890 filesList.push_back(pair<Utf8Str, ULONG>(strManifestFile, 0));
1891 else if (RT_FAILURE(vrc))
1892 {
1893 if(vrc == VERR_S3_CANCELED)
1894 throw S_OK; /* todo: !!!!!!!!!!!!! */
1895 else if(vrc == VERR_S3_NOT_FOUND)
1896 vrc = VINF_SUCCESS; /* Not found is ok */
1897 else if(vrc == VERR_S3_ACCESS_DENIED)
1898 throw setError(E_ACCESSDENIED,
1899 tr("Cannot download file '%s' from S3 storage server (Access denied). Make sure that your credentials are right. Also check that your host clock is properly synced"), pszFilename);
1900 else
1901 throw setError(VBOX_E_IPRT_ERROR,
1902 tr("Cannot download file '%s' from S3 storage server (%Rrc)"), pszFilename, vrc);
1903 }
1904
1905 /* Close the connection early */
1906 RTS3Destroy(hS3);
1907 hS3 = NIL_RTS3;
1908
1909 if (!pTask->pProgress.isNull())
1910 pTask->pProgress->SetNextOperation(BstrFmt(tr("Importing appliance")), m->ulWeightPerOperation);
1911
1912 ComObjPtr<Progress> progress;
1913 /* Import the whole temporary OVF & the disk images */
1914 LocationInfo li;
1915 li.strPath = strTmpOvf;
1916 rc = importImpl(li, progress);
1917 if (FAILED(rc)) throw rc;
1918
1919 /* Unlock the appliance for the fs import thread */
1920 appLock.release();
1921 /* Wait until the import is done, but report the progress back to the
1922 caller */
1923 ComPtr<IProgress> progressInt(progress);
1924 waitForAsyncProgress(pTask->pProgress, progressInt); /* Any errors will be thrown */
1925
1926 /* Again lock the appliance for the next steps */
1927 appLock.acquire();
1928 }
1929 catch(HRESULT aRC)
1930 {
1931 rc = aRC;
1932 }
1933 /* Cleanup */
1934 RTS3Destroy(hS3);
1935 /* Delete all files which where temporary created */
1936 for (list< pair<Utf8Str, ULONG> >::const_iterator it1 = filesList.begin(); it1 != filesList.end(); ++it1)
1937 {
1938 const char *pszFilePath = (*it1).first.c_str();
1939 if (RTPathExists(pszFilePath))
1940 {
1941 vrc = RTFileDelete(pszFilePath);
1942 if(RT_FAILURE(vrc))
1943 rc = setError(VBOX_E_FILE_ERROR,
1944 tr("Cannot delete file '%s' (%Rrc)"), pszFilePath, vrc);
1945 }
1946 }
1947 /* Delete the temporary directory */
1948 if (RTPathExists(pszTmpDir))
1949 {
1950 vrc = RTDirRemove(pszTmpDir);
1951 if(RT_FAILURE(vrc))
1952 rc = setError(VBOX_E_FILE_ERROR,
1953 tr("Cannot delete temporary directory '%s' (%Rrc)"), pszTmpDir, vrc);
1954 }
1955 if (pszTmpDir)
1956 RTStrFree(pszTmpDir);
1957
1958 LogFlowFunc(("rc=%Rhrc\n", rc));
1959 LogFlowFuncLeave();
1960
1961 return rc;
1962}
1963
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette