VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/ApplianceImplImport.cpp@ 42129

Last change on this file since 42129 was 42129, checked in by vboxsync, 13 years ago

Main/VirtualBox+Machine: add support for VM groups (incomplete, saving of settings is not implemented), remaining code adjusted accordingly, use better parameter checking macros, make XSLT generators process setter attributes using safearrays correctly, various cleanups and warning fixes
Frontends/VBoxManage: first traces of groups support

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 120.6 KB
Line 
1/* $Id: ApplianceImplImport.cpp 42129 2012-07-12 17:32:31Z vboxsync $ */
2/** @file
3 *
4 * IAppliance and IVirtualSystem COM class implementations.
5 */
6
7/*
8 * Copyright (C) 2008-2012 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.215389.xyz. 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
19#include <iprt/path.h>
20#include <iprt/dir.h>
21#include <iprt/file.h>
22#include <iprt/s3.h>
23#include <iprt/sha.h>
24#include <iprt/manifest.h>
25#include <iprt/tar.h>
26#include <iprt/stream.h>
27
28#include <VBox/vd.h>
29#include <VBox/com/array.h>
30
31#include "ApplianceImpl.h"
32#include "VirtualBoxImpl.h"
33#include "GuestOSTypeImpl.h"
34#include "ProgressImpl.h"
35#include "MachineImpl.h"
36#include "MediumImpl.h"
37#include "MediumFormatImpl.h"
38#include "SystemPropertiesImpl.h"
39#include "HostImpl.h"
40
41#include "AutoCaller.h"
42#include "Logging.h"
43
44#include "ApplianceImplPrivate.h"
45
46#include <VBox/param.h>
47#include <VBox/version.h>
48#include <VBox/settings.h>
49
50using namespace std;
51
52////////////////////////////////////////////////////////////////////////////////
53//
54// IAppliance public methods
55//
56////////////////////////////////////////////////////////////////////////////////
57
58/**
59 * Public method implementation. This opens the OVF with ovfreader.cpp.
60 * Thread implementation is in Appliance::readImpl().
61 *
62 * @param path
63 * @return
64 */
65STDMETHODIMP Appliance::Read(IN_BSTR path, IProgress **aProgress)
66{
67 if (!path) return E_POINTER;
68 CheckComArgOutPointerValid(aProgress);
69
70 AutoCaller autoCaller(this);
71 if (FAILED(autoCaller.rc())) return autoCaller.rc();
72
73 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
74
75 if (!isApplianceIdle())
76 return E_ACCESSDENIED;
77
78 if (m->pReader)
79 {
80 delete m->pReader;
81 m->pReader = NULL;
82 }
83
84 // see if we can handle this file; for now we insist it has an ovf/ova extension
85 Utf8Str strPath (path);
86 if (!( strPath.endsWith(".ovf", Utf8Str::CaseInsensitive)
87 || strPath.endsWith(".ova", Utf8Str::CaseInsensitive)))
88 return setError(VBOX_E_FILE_ERROR,
89 tr("Appliance file must have .ovf extension"));
90
91 ComObjPtr<Progress> progress;
92 HRESULT rc = S_OK;
93 try
94 {
95 /* Parse all necessary info out of the URI */
96 parseURI(strPath, m->locInfo);
97 rc = readImpl(m->locInfo, progress);
98 }
99 catch (HRESULT aRC)
100 {
101 rc = aRC;
102 }
103
104 if (SUCCEEDED(rc))
105 /* Return progress to the caller */
106 progress.queryInterfaceTo(aProgress);
107
108 return S_OK;
109}
110
111/**
112 * Public method implementation. This looks at the output of ovfreader.cpp and creates
113 * VirtualSystemDescription instances.
114 * @return
115 */
116STDMETHODIMP Appliance::Interpret()
117{
118 // @todo:
119 // - don't use COM methods but the methods directly (faster, but needs appropriate locking of that objects itself (s. HardDisk))
120 // - Appropriate handle errors like not supported file formats
121 AutoCaller autoCaller(this);
122 if (FAILED(autoCaller.rc())) return autoCaller.rc();
123
124 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
125
126 if (!isApplianceIdle())
127 return E_ACCESSDENIED;
128
129 HRESULT rc = S_OK;
130
131 /* Clear any previous virtual system descriptions */
132 m->virtualSystemDescriptions.clear();
133
134 if (!m->pReader)
135 return setError(E_FAIL,
136 tr("Cannot interpret appliance without reading it first (call read() before interpret())"));
137
138 // Change the appliance state so we can safely leave the lock while doing time-consuming
139 // disk imports; also the below method calls do all kinds of locking which conflicts with
140 // the appliance object lock
141 m->state = Data::ApplianceImporting;
142 alock.release();
143
144 /* Try/catch so we can clean up on error */
145 try
146 {
147 list<ovf::VirtualSystem>::const_iterator it;
148 /* Iterate through all virtual systems */
149 for (it = m->pReader->m_llVirtualSystems.begin();
150 it != m->pReader->m_llVirtualSystems.end();
151 ++it)
152 {
153 const ovf::VirtualSystem &vsysThis = *it;
154
155 ComObjPtr<VirtualSystemDescription> pNewDesc;
156 rc = pNewDesc.createObject();
157 if (FAILED(rc)) throw rc;
158 rc = pNewDesc->init();
159 if (FAILED(rc)) throw rc;
160
161 // if the virtual system in OVF had a <vbox:Machine> element, have the
162 // VirtualBox settings code parse that XML now
163 if (vsysThis.pelmVboxMachine)
164 pNewDesc->importVboxMachineXML(*vsysThis.pelmVboxMachine);
165
166 // Guest OS type
167 // This is taken from one of three places, in this order:
168 Utf8Str strOsTypeVBox;
169 Utf8StrFmt strCIMOSType("%RU32", (uint32_t)vsysThis.cimos);
170 // 1) If there is a <vbox:Machine>, then use the type from there.
171 if ( vsysThis.pelmVboxMachine
172 && pNewDesc->m->pConfig->machineUserData.strOsType.isNotEmpty()
173 )
174 strOsTypeVBox = pNewDesc->m->pConfig->machineUserData.strOsType;
175 // 2) Otherwise, if there is OperatingSystemSection/vbox:OSType, use that one.
176 else if (vsysThis.strTypeVbox.isNotEmpty()) // OVFReader has found vbox:OSType
177 strOsTypeVBox = vsysThis.strTypeVbox;
178 // 3) Otherwise, make a best guess what the vbox type is from the OVF (CIM) OS type.
179 else
180 convertCIMOSType2VBoxOSType(strOsTypeVBox, vsysThis.cimos, vsysThis.strCimosDesc);
181 pNewDesc->addEntry(VirtualSystemDescriptionType_OS,
182 "",
183 strCIMOSType,
184 strOsTypeVBox);
185
186 /* VM name */
187 Utf8Str nameVBox;
188 /* If there is a <vbox:Machine>, we always prefer the setting from there. */
189 if ( vsysThis.pelmVboxMachine
190 && pNewDesc->m->pConfig->machineUserData.strName.isNotEmpty())
191 nameVBox = pNewDesc->m->pConfig->machineUserData.strName;
192 else
193 nameVBox = vsysThis.strName;
194 /* If there isn't any name specified create a default one out
195 * of the OS type */
196 if (nameVBox.isEmpty())
197 nameVBox = strOsTypeVBox;
198 searchUniqueVMName(nameVBox);
199 pNewDesc->addEntry(VirtualSystemDescriptionType_Name,
200 "",
201 vsysThis.strName,
202 nameVBox);
203
204 /* Based on the VM name, create a target machine path. */
205 Bstr bstrMachineFilename;
206 rc = mVirtualBox->ComposeMachineFilename(Bstr(nameVBox).raw(),
207 NULL /* aGroup */,
208 NULL /* aBaseFolder */,
209 bstrMachineFilename.asOutParam());
210 if (FAILED(rc)) throw rc;
211 /* Determine the machine folder from that */
212 Utf8Str strMachineFolder = Utf8Str(bstrMachineFilename).stripFilename();
213
214 /* VM Product */
215 if (!vsysThis.strProduct.isEmpty())
216 pNewDesc->addEntry(VirtualSystemDescriptionType_Product,
217 "",
218 vsysThis.strProduct,
219 vsysThis.strProduct);
220
221 /* VM Vendor */
222 if (!vsysThis.strVendor.isEmpty())
223 pNewDesc->addEntry(VirtualSystemDescriptionType_Vendor,
224 "",
225 vsysThis.strVendor,
226 vsysThis.strVendor);
227
228 /* VM Version */
229 if (!vsysThis.strVersion.isEmpty())
230 pNewDesc->addEntry(VirtualSystemDescriptionType_Version,
231 "",
232 vsysThis.strVersion,
233 vsysThis.strVersion);
234
235 /* VM ProductUrl */
236 if (!vsysThis.strProductUrl.isEmpty())
237 pNewDesc->addEntry(VirtualSystemDescriptionType_ProductUrl,
238 "",
239 vsysThis.strProductUrl,
240 vsysThis.strProductUrl);
241
242 /* VM VendorUrl */
243 if (!vsysThis.strVendorUrl.isEmpty())
244 pNewDesc->addEntry(VirtualSystemDescriptionType_VendorUrl,
245 "",
246 vsysThis.strVendorUrl,
247 vsysThis.strVendorUrl);
248
249 /* VM description */
250 if (!vsysThis.strDescription.isEmpty())
251 pNewDesc->addEntry(VirtualSystemDescriptionType_Description,
252 "",
253 vsysThis.strDescription,
254 vsysThis.strDescription);
255
256 /* VM license */
257 if (!vsysThis.strLicenseText.isEmpty())
258 pNewDesc->addEntry(VirtualSystemDescriptionType_License,
259 "",
260 vsysThis.strLicenseText,
261 vsysThis.strLicenseText);
262
263 /* Now that we know the OS type, get our internal defaults based on that. */
264 ComPtr<IGuestOSType> pGuestOSType;
265 rc = mVirtualBox->GetGuestOSType(Bstr(strOsTypeVBox).raw(), pGuestOSType.asOutParam());
266 if (FAILED(rc)) throw rc;
267
268 /* CPU count */
269 ULONG cpuCountVBox;
270 /* If there is a <vbox:Machine>, we always prefer the setting from there. */
271 if ( vsysThis.pelmVboxMachine
272 && pNewDesc->m->pConfig->hardwareMachine.cCPUs)
273 cpuCountVBox = pNewDesc->m->pConfig->hardwareMachine.cCPUs;
274 else
275 cpuCountVBox = vsysThis.cCPUs;
276 /* Check for the constraints */
277 if (cpuCountVBox > SchemaDefs::MaxCPUCount)
278 {
279 addWarning(tr("The virtual system \"%s\" claims support for %u CPU's, but VirtualBox has support for max %u CPU's only."),
280 vsysThis.strName.c_str(), cpuCountVBox, SchemaDefs::MaxCPUCount);
281 cpuCountVBox = SchemaDefs::MaxCPUCount;
282 }
283 if (vsysThis.cCPUs == 0)
284 cpuCountVBox = 1;
285 pNewDesc->addEntry(VirtualSystemDescriptionType_CPU,
286 "",
287 Utf8StrFmt("%RU32", (uint32_t)vsysThis.cCPUs),
288 Utf8StrFmt("%RU32", (uint32_t)cpuCountVBox));
289
290 /* RAM */
291 uint64_t ullMemSizeVBox;
292 /* If there is a <vbox:Machine>, we always prefer the setting from there. */
293 if ( vsysThis.pelmVboxMachine
294 && pNewDesc->m->pConfig->hardwareMachine.ulMemorySizeMB)
295 ullMemSizeVBox = pNewDesc->m->pConfig->hardwareMachine.ulMemorySizeMB;
296 else
297 ullMemSizeVBox = vsysThis.ullMemorySize / _1M;
298 /* Check for the constraints */
299 if ( ullMemSizeVBox != 0
300 && ( ullMemSizeVBox < MM_RAM_MIN_IN_MB
301 || ullMemSizeVBox > MM_RAM_MAX_IN_MB
302 )
303 )
304 {
305 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."),
306 vsysThis.strName.c_str(), ullMemSizeVBox, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
307 ullMemSizeVBox = RT_MIN(RT_MAX(ullMemSizeVBox, MM_RAM_MIN_IN_MB), MM_RAM_MAX_IN_MB);
308 }
309 if (vsysThis.ullMemorySize == 0)
310 {
311 /* If the RAM of the OVF is zero, use our predefined values */
312 ULONG memSizeVBox2;
313 rc = pGuestOSType->COMGETTER(RecommendedRAM)(&memSizeVBox2);
314 if (FAILED(rc)) throw rc;
315 /* VBox stores that in MByte */
316 ullMemSizeVBox = (uint64_t)memSizeVBox2;
317 }
318 pNewDesc->addEntry(VirtualSystemDescriptionType_Memory,
319 "",
320 Utf8StrFmt("%RU64", (uint64_t)vsysThis.ullMemorySize),
321 Utf8StrFmt("%RU64", (uint64_t)ullMemSizeVBox));
322
323 /* Audio */
324 Utf8Str strSoundCard;
325 Utf8Str strSoundCardOrig;
326 /* If there is a <vbox:Machine>, we always prefer the setting from there. */
327 if ( vsysThis.pelmVboxMachine
328 && pNewDesc->m->pConfig->hardwareMachine.audioAdapter.fEnabled)
329 strSoundCard = Utf8StrFmt("%RU32", (uint32_t)pNewDesc->m->pConfig->hardwareMachine.audioAdapter.controllerType);
330 else if (vsysThis.strSoundCardType.isNotEmpty())
331 {
332 /* Set the AC97 always for the simple OVF case.
333 * @todo: figure out the hardware which could be possible */
334 strSoundCard = Utf8StrFmt("%RU32", (uint32_t)AudioControllerType_AC97);
335 strSoundCardOrig = vsysThis.strSoundCardType;
336 }
337 if (strSoundCard.isNotEmpty())
338 pNewDesc->addEntry(VirtualSystemDescriptionType_SoundCard,
339 "",
340 strSoundCardOrig,
341 strSoundCard);
342
343#ifdef VBOX_WITH_USB
344 /* USB Controller */
345 /* If there is a <vbox:Machine>, we always prefer the setting from there. */
346 if ( ( vsysThis.pelmVboxMachine
347 && pNewDesc->m->pConfig->hardwareMachine.usbController.fEnabled)
348 || vsysThis.fHasUsbController)
349 pNewDesc->addEntry(VirtualSystemDescriptionType_USBController, "", "", "");
350#endif /* VBOX_WITH_USB */
351
352 /* Network Controller */
353 /* If there is a <vbox:Machine>, we always prefer the setting from there. */
354 if (vsysThis.pelmVboxMachine)
355 {
356 uint32_t maxNetworkAdapters = Global::getMaxNetworkAdapters(pNewDesc->m->pConfig->hardwareMachine.chipsetType);
357
358 const settings::NetworkAdaptersList &llNetworkAdapters = pNewDesc->m->pConfig->hardwareMachine.llNetworkAdapters;
359 /* Check for the constrains */
360 if (llNetworkAdapters.size() > maxNetworkAdapters)
361 addWarning(tr("The virtual system \"%s\" claims support for %zu network adapters, but VirtualBox has support for max %u network adapter only."),
362 vsysThis.strName.c_str(), llNetworkAdapters.size(), maxNetworkAdapters);
363 /* Iterate through all network adapters. */
364 settings::NetworkAdaptersList::const_iterator it1;
365 size_t a = 0;
366 for (it1 = llNetworkAdapters.begin();
367 it1 != llNetworkAdapters.end() && a < maxNetworkAdapters;
368 ++it1, ++a)
369 {
370 if (it1->fEnabled)
371 {
372 Utf8Str strMode = convertNetworkAttachmentTypeToString(it1->mode);
373 pNewDesc->addEntry(VirtualSystemDescriptionType_NetworkAdapter,
374 "", // ref
375 strMode, // orig
376 Utf8StrFmt("%RU32", (uint32_t)it1->type), // conf
377 0,
378 Utf8StrFmt("slot=%RU32;type=%s", it1->ulSlot, strMode.c_str())); // extra conf
379 }
380 }
381 }
382 /* else we use the ovf configuration. */
383 else if (size_t cEthernetAdapters = vsysThis.llEthernetAdapters.size() > 0)
384 {
385 uint32_t maxNetworkAdapters = Global::getMaxNetworkAdapters(ChipsetType_PIIX3);
386
387 /* Check for the constrains */
388 if (cEthernetAdapters > maxNetworkAdapters)
389 addWarning(tr("The virtual system \"%s\" claims support for %zu network adapters, but VirtualBox has support for max %u network adapter only."),
390 vsysThis.strName.c_str(), cEthernetAdapters, maxNetworkAdapters);
391
392 /* Get the default network adapter type for the selected guest OS */
393 NetworkAdapterType_T defaultAdapterVBox = NetworkAdapterType_Am79C970A;
394 rc = pGuestOSType->COMGETTER(AdapterType)(&defaultAdapterVBox);
395 if (FAILED(rc)) throw rc;
396
397 ovf::EthernetAdaptersList::const_iterator itEA;
398 /* Iterate through all abstract networks. Ignore network cards
399 * which exceed the limit of VirtualBox. */
400 size_t a = 0;
401 for (itEA = vsysThis.llEthernetAdapters.begin();
402 itEA != vsysThis.llEthernetAdapters.end() && a < maxNetworkAdapters;
403 ++itEA, ++a)
404 {
405 const ovf::EthernetAdapter &ea = *itEA; // logical network to connect to
406 Utf8Str strNetwork = ea.strNetworkName;
407 // make sure it's one of these two
408 if ( (strNetwork.compare("Null", Utf8Str::CaseInsensitive))
409 && (strNetwork.compare("NAT", Utf8Str::CaseInsensitive))
410 && (strNetwork.compare("Bridged", Utf8Str::CaseInsensitive))
411 && (strNetwork.compare("Internal", Utf8Str::CaseInsensitive))
412 && (strNetwork.compare("HostOnly", Utf8Str::CaseInsensitive))
413 && (strNetwork.compare("Generic", Utf8Str::CaseInsensitive))
414 )
415 strNetwork = "Bridged"; // VMware assumes this is the default apparently
416
417 /* Figure out the hardware type */
418 NetworkAdapterType_T nwAdapterVBox = defaultAdapterVBox;
419 if (!ea.strAdapterType.compare("PCNet32", Utf8Str::CaseInsensitive))
420 {
421 /* If the default adapter is already one of the two
422 * PCNet adapters use the default one. If not use the
423 * Am79C970A as fallback. */
424 if (!(defaultAdapterVBox == NetworkAdapterType_Am79C970A ||
425 defaultAdapterVBox == NetworkAdapterType_Am79C973))
426 nwAdapterVBox = NetworkAdapterType_Am79C970A;
427 }
428#ifdef VBOX_WITH_E1000
429 /* VMWare accidentally write this with VirtualCenter 3.5,
430 so make sure in this case always to use the VMWare one */
431 else if (!ea.strAdapterType.compare("E10000", Utf8Str::CaseInsensitive))
432 nwAdapterVBox = NetworkAdapterType_I82545EM;
433 else if (!ea.strAdapterType.compare("E1000", Utf8Str::CaseInsensitive))
434 {
435 /* Check if this OVF was written by VirtualBox */
436 if (Utf8Str(vsysThis.strVirtualSystemType).contains("virtualbox", Utf8Str::CaseInsensitive))
437 {
438 /* If the default adapter is already one of the three
439 * E1000 adapters use the default one. If not use the
440 * I82545EM as fallback. */
441 if (!(defaultAdapterVBox == NetworkAdapterType_I82540EM ||
442 defaultAdapterVBox == NetworkAdapterType_I82543GC ||
443 defaultAdapterVBox == NetworkAdapterType_I82545EM))
444 nwAdapterVBox = NetworkAdapterType_I82540EM;
445 }
446 else
447 /* Always use this one since it's what VMware uses */
448 nwAdapterVBox = NetworkAdapterType_I82545EM;
449 }
450#endif /* VBOX_WITH_E1000 */
451
452 pNewDesc->addEntry(VirtualSystemDescriptionType_NetworkAdapter,
453 "", // ref
454 ea.strNetworkName, // orig
455 Utf8StrFmt("%RU32", (uint32_t)nwAdapterVBox), // conf
456 0,
457 Utf8StrFmt("type=%s", strNetwork.c_str())); // extra conf
458 }
459 }
460
461 /* If there is a <vbox:Machine>, we always prefer the setting from there. */
462 bool fFloppy = false;
463 bool fDVD = false;
464 if (vsysThis.pelmVboxMachine)
465 {
466 settings::StorageControllersList &llControllers = pNewDesc->m->pConfig->storageMachine.llStorageControllers;
467 settings::StorageControllersList::iterator it3;
468 for (it3 = llControllers.begin();
469 it3 != llControllers.end();
470 ++it3)
471 {
472 settings::AttachedDevicesList &llAttachments = it3->llAttachedDevices;
473 settings::AttachedDevicesList::iterator it4;
474 for (it4 = llAttachments.begin();
475 it4 != llAttachments.end();
476 ++it4)
477 {
478 fDVD |= it4->deviceType == DeviceType_DVD;
479 fFloppy |= it4->deviceType == DeviceType_Floppy;
480 if (fFloppy && fDVD)
481 break;
482 }
483 if (fFloppy && fDVD)
484 break;
485 }
486 }
487 else
488 {
489 fFloppy = vsysThis.fHasFloppyDrive;
490 fDVD = vsysThis.fHasCdromDrive;
491 }
492 /* Floppy Drive */
493 if (fFloppy)
494 pNewDesc->addEntry(VirtualSystemDescriptionType_Floppy, "", "", "");
495 /* CD Drive */
496 if (fDVD)
497 pNewDesc->addEntry(VirtualSystemDescriptionType_CDROM, "", "", "");
498
499 /* Hard disk Controller */
500 uint16_t cIDEused = 0;
501 uint16_t cSATAused = 0; NOREF(cSATAused);
502 uint16_t cSCSIused = 0; NOREF(cSCSIused);
503 ovf::ControllersMap::const_iterator hdcIt;
504 /* Iterate through all hard disk controllers */
505 for (hdcIt = vsysThis.mapControllers.begin();
506 hdcIt != vsysThis.mapControllers.end();
507 ++hdcIt)
508 {
509 const ovf::HardDiskController &hdc = hdcIt->second;
510 Utf8Str strControllerID = Utf8StrFmt("%RI32", (uint32_t)hdc.idController);
511
512 switch (hdc.system)
513 {
514 case ovf::HardDiskController::IDE:
515 /* Check for the constrains */
516 if (cIDEused < 4)
517 {
518 // @todo: figure out the IDE types
519 /* Use PIIX4 as default */
520 Utf8Str strType = "PIIX4";
521 if (!hdc.strControllerType.compare("PIIX3", Utf8Str::CaseInsensitive))
522 strType = "PIIX3";
523 else if (!hdc.strControllerType.compare("ICH6", Utf8Str::CaseInsensitive))
524 strType = "ICH6";
525 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerIDE,
526 strControllerID, // strRef
527 hdc.strControllerType, // aOvfValue
528 strType); // aVboxValue
529 }
530 else
531 /* Warn only once */
532 if (cIDEused == 2)
533 addWarning(tr("The virtual \"%s\" system requests support for more than two IDE controller channels, but VirtualBox supports only two."),
534 vsysThis.strName.c_str());
535
536 ++cIDEused;
537 break;
538
539 case ovf::HardDiskController::SATA:
540 /* Check for the constrains */
541 if (cSATAused < 1)
542 {
543 // @todo: figure out the SATA types
544 /* We only support a plain AHCI controller, so use them always */
545 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSATA,
546 strControllerID,
547 hdc.strControllerType,
548 "AHCI");
549 }
550 else
551 {
552 /* Warn only once */
553 if (cSATAused == 1)
554 addWarning(tr("The virtual system \"%s\" requests support for more than one SATA controller, but VirtualBox has support for only one"),
555 vsysThis.strName.c_str());
556
557 }
558 ++cSATAused;
559 break;
560
561 case ovf::HardDiskController::SCSI:
562 /* Check for the constrains */
563 if (cSCSIused < 1)
564 {
565 VirtualSystemDescriptionType_T vsdet = VirtualSystemDescriptionType_HardDiskControllerSCSI;
566 Utf8Str hdcController = "LsiLogic";
567 if (!hdc.strControllerType.compare("lsilogicsas", Utf8Str::CaseInsensitive))
568 {
569 // OVF considers SAS a variant of SCSI but VirtualBox considers it a class of its own
570 vsdet = VirtualSystemDescriptionType_HardDiskControllerSAS;
571 hdcController = "LsiLogicSas";
572 }
573 else if (!hdc.strControllerType.compare("BusLogic", Utf8Str::CaseInsensitive))
574 hdcController = "BusLogic";
575 pNewDesc->addEntry(vsdet,
576 strControllerID,
577 hdc.strControllerType,
578 hdcController);
579 }
580 else
581 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."),
582 vsysThis.strName.c_str(),
583 hdc.strControllerType.c_str(),
584 strControllerID.c_str());
585 ++cSCSIused;
586 break;
587 }
588 }
589
590 /* Hard disks */
591 if (vsysThis.mapVirtualDisks.size() > 0)
592 {
593 ovf::VirtualDisksMap::const_iterator itVD;
594 /* Iterate through all hard disks ()*/
595 for (itVD = vsysThis.mapVirtualDisks.begin();
596 itVD != vsysThis.mapVirtualDisks.end();
597 ++itVD)
598 {
599 const ovf::VirtualDisk &hd = itVD->second;
600 /* Get the associated disk image */
601 const ovf::DiskImage &di = m->pReader->m_mapDisks[hd.strDiskId];
602
603 // @todo:
604 // - figure out all possible vmdk formats we also support
605 // - figure out if there is a url specifier for vhd already
606 // - we need a url specifier for the vdi format
607 if ( di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#sparse", Utf8Str::CaseInsensitive)
608 || di.strFormat.compare("http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized", Utf8Str::CaseInsensitive)
609 || di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#compressed", Utf8Str::CaseInsensitive)
610 || di.strFormat.compare("http://www.vmware.com/interfaces/specifications/vmdk.html#compressed", Utf8Str::CaseInsensitive)
611 )
612 {
613 /* If the href is empty use the VM name as filename */
614 Utf8Str strFilename = di.strHref;
615 if (!strFilename.length())
616 strFilename = Utf8StrFmt("%s.vmdk", nameVBox.c_str());
617
618 Utf8Str strTargetPath = Utf8Str(strMachineFolder)
619 .append(RTPATH_DELIMITER)
620 .append(di.strHref);
621 searchUniqueDiskImageFilePath(strTargetPath);
622
623 /* find the description for the hard disk controller
624 * that has the same ID as hd.idController */
625 const VirtualSystemDescriptionEntry *pController;
626 if (!(pController = pNewDesc->findControllerFromID(hd.idController)))
627 throw setError(E_FAIL,
628 tr("Cannot find hard disk controller with OVF instance ID %RI32 to which disk \"%s\" should be attached"),
629 hd.idController,
630 di.strHref.c_str());
631
632 /* controller to attach to, and the bus within that controller */
633 Utf8StrFmt strExtraConfig("controller=%RI16;channel=%RI16",
634 pController->ulIndex,
635 hd.ulAddressOnParent);
636 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskImage,
637 hd.strDiskId,
638 di.strHref,
639 strTargetPath,
640 di.ulSuggestedSizeMB,
641 strExtraConfig);
642 }
643 else
644 throw setError(VBOX_E_FILE_ERROR,
645 tr("Unsupported format for virtual disk image in OVF: \"%s\"", di.strFormat.c_str()));
646 }
647 }
648
649 m->virtualSystemDescriptions.push_back(pNewDesc);
650 }
651 }
652 catch (HRESULT aRC)
653 {
654 /* On error we clear the list & return */
655 m->virtualSystemDescriptions.clear();
656 rc = aRC;
657 }
658
659 // reset the appliance state
660 alock.acquire();
661 m->state = Data::ApplianceIdle;
662
663 return rc;
664}
665
666/**
667 * Public method implementation. This creates one or more new machines according to the
668 * VirtualSystemScription instances created by Appliance::Interpret().
669 * Thread implementation is in Appliance::importImpl().
670 * @param aProgress
671 * @return
672 */
673STDMETHODIMP Appliance::ImportMachines(ComSafeArrayIn(ImportOptions_T, options), IProgress **aProgress)
674{
675 CheckComArgOutPointerValid(aProgress);
676
677 AutoCaller autoCaller(this);
678 if (FAILED(autoCaller.rc())) return autoCaller.rc();
679
680 if (options != NULL)
681 m->optList = com::SafeArray<ImportOptions_T>(ComSafeArrayInArg(options)).toList();
682
683 AssertReturn(!(m->optList.contains(ImportOptions_KeepAllMACs) && m->optList.contains(ImportOptions_KeepNATMACs)), E_INVALIDARG);
684
685 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
686
687 // do not allow entering this method if the appliance is busy reading or writing
688 if (!isApplianceIdle())
689 return E_ACCESSDENIED;
690
691 if (!m->pReader)
692 return setError(E_FAIL,
693 tr("Cannot import machines without reading it first (call read() before importMachines())"));
694
695 ComObjPtr<Progress> progress;
696 HRESULT rc = S_OK;
697 try
698 {
699 rc = importImpl(m->locInfo, progress);
700 }
701 catch (HRESULT aRC)
702 {
703 rc = aRC;
704 }
705
706 if (SUCCEEDED(rc))
707 /* Return progress to the caller */
708 progress.queryInterfaceTo(aProgress);
709
710 return rc;
711}
712
713////////////////////////////////////////////////////////////////////////////////
714//
715// Appliance private methods
716//
717////////////////////////////////////////////////////////////////////////////////
718
719
720/*******************************************************************************
721 * Read stuff
722 ******************************************************************************/
723
724/**
725 * Implementation for reading an OVF. This starts a new thread which will call
726 * Appliance::taskThreadImportOrExport() which will then call readFS() or readS3().
727 * This will then open the OVF with ovfreader.cpp.
728 *
729 * This is in a separate private method because it is used from three locations:
730 *
731 * 1) from the public Appliance::Read().
732 *
733 * 2) in a second worker thread; in that case, Appliance::ImportMachines() called Appliance::importImpl(), which
734 * called Appliance::readFSOVA(), which called Appliance::importImpl(), which then called this again.
735 *
736 * 3) from Appliance::readS3(), which got called from a previous instance of Appliance::taskThreadImportOrExport().
737 *
738 * @param aLocInfo
739 * @param aProgress
740 * @return
741 */
742HRESULT Appliance::readImpl(const LocationInfo &aLocInfo, ComObjPtr<Progress> &aProgress)
743{
744 BstrFmt bstrDesc = BstrFmt(tr("Reading appliance '%s'"),
745 aLocInfo.strPath.c_str());
746 HRESULT rc;
747 /* Create the progress object */
748 aProgress.createObject();
749 if (aLocInfo.storageType == VFSType_File)
750 /* 1 operation only */
751 rc = aProgress->init(mVirtualBox, static_cast<IAppliance*>(this),
752 bstrDesc.raw(),
753 TRUE /* aCancelable */);
754 else
755 /* 4/5 is downloading, 1/5 is reading */
756 rc = aProgress->init(mVirtualBox, static_cast<IAppliance*>(this),
757 bstrDesc.raw(),
758 TRUE /* aCancelable */,
759 2, // ULONG cOperations,
760 5, // ULONG ulTotalOperationsWeight,
761 BstrFmt(tr("Download appliance '%s'"),
762 aLocInfo.strPath.c_str()).raw(), // CBSTR bstrFirstOperationDescription,
763 4); // ULONG ulFirstOperationWeight,
764 if (FAILED(rc)) throw rc;
765
766 /* Initialize our worker task */
767 std::auto_ptr<TaskOVF> task(new TaskOVF(this, TaskOVF::Read, aLocInfo, aProgress));
768
769 rc = task->startThread();
770 if (FAILED(rc)) throw rc;
771
772 /* Don't destruct on success */
773 task.release();
774
775 return rc;
776}
777
778/**
779 * Actual worker code for reading an OVF from disk. This is called from Appliance::taskThreadImportOrExport()
780 * and therefore runs on the OVF read worker thread. This opens the OVF with ovfreader.cpp.
781 *
782 * This runs in two contexts:
783 *
784 * 1) in a first worker thread; in that case, Appliance::Read() called Appliance::readImpl();
785 *
786 * 2) in a second worker thread; in that case, Appliance::Read() called Appliance::readImpl(), which
787 * called Appliance::readS3(), which called Appliance::readImpl(), which then called this.
788 *
789 * @param pTask
790 * @return
791 */
792HRESULT Appliance::readFS(TaskOVF *pTask)
793{
794 LogFlowFuncEnter();
795 LogFlowFunc(("Appliance %p\n", this));
796
797 AutoCaller autoCaller(this);
798 if (FAILED(autoCaller.rc())) return autoCaller.rc();
799
800 AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);
801
802 HRESULT rc = S_OK;
803
804 if (pTask->locInfo.strPath.endsWith(".ovf", Utf8Str::CaseInsensitive))
805 rc = readFSOVF(pTask);
806 else
807 rc = readFSOVA(pTask);
808
809 LogFlowFunc(("rc=%Rhrc\n", rc));
810 LogFlowFuncLeave();
811
812 return rc;
813}
814
815HRESULT Appliance::readFSOVF(TaskOVF *pTask)
816{
817 LogFlowFuncEnter();
818
819 HRESULT rc = S_OK;
820
821 PVDINTERFACEIO pShaIo = 0;
822 PVDINTERFACEIO pFileIo = 0;
823 do
824 {
825 pShaIo = ShaCreateInterface();
826 if (!pShaIo)
827 {
828 rc = E_OUTOFMEMORY;
829 break;
830 }
831 pFileIo = FileCreateInterface();
832 if (!pFileIo)
833 {
834 rc = E_OUTOFMEMORY;
835 break;
836 }
837 SHASTORAGE storage;
838 RT_ZERO(storage);
839 int vrc = VDInterfaceAdd(&pFileIo->Core, "Appliance::IOFile",
840 VDINTERFACETYPE_IO, 0, sizeof(VDINTERFACEIO),
841 &storage.pVDImageIfaces);
842 if (RT_FAILURE(vrc))
843 {
844 rc = setError(VBOX_E_IPRT_ERROR, "Creation of the VD interface failed (%Rrc)", vrc);
845 break;
846 }
847
848 rc = readFSImpl(pTask, pTask->locInfo.strPath, pShaIo, &storage);
849 }while(0);
850
851 /* Cleanup */
852 if (pShaIo)
853 RTMemFree(pShaIo);
854 if (pFileIo)
855 RTMemFree(pFileIo);
856
857 LogFlowFunc(("rc=%Rhrc\n", rc));
858 LogFlowFuncLeave();
859
860 return rc;
861}
862
863HRESULT Appliance::readFSOVA(TaskOVF *pTask)
864{
865 LogFlowFuncEnter();
866
867 RTTAR tar;
868 int vrc = RTTarOpen(&tar, pTask->locInfo.strPath.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, true);
869 if (RT_FAILURE(vrc))
870 return setError(VBOX_E_FILE_ERROR,
871 tr("Could not open OVA file '%s' (%Rrc)"),
872 pTask->locInfo.strPath.c_str(), vrc);
873
874 HRESULT rc = S_OK;
875
876 PVDINTERFACEIO pShaIo = 0;
877 PVDINTERFACEIO pTarIo = 0;
878 char *pszFilename = 0;
879 do
880 {
881 vrc = RTTarCurrentFile(tar, &pszFilename);
882 if (RT_FAILURE(vrc))
883 {
884 rc = VBOX_E_FILE_ERROR;
885 break;
886 }
887 pShaIo = ShaCreateInterface();
888 if (!pShaIo)
889 {
890 rc = E_OUTOFMEMORY;
891 break;
892 }
893 pTarIo = TarCreateInterface();
894 if (!pTarIo)
895 {
896 rc = E_OUTOFMEMORY;
897 break;
898 }
899 SHASTORAGE storage;
900 RT_ZERO(storage);
901 vrc = VDInterfaceAdd(&pTarIo->Core, "Appliance::IOTar",
902 VDINTERFACETYPE_IO, tar, sizeof(VDINTERFACEIO),
903 &storage.pVDImageIfaces);
904 if (RT_FAILURE(vrc))
905 {
906 rc = setError(VBOX_E_IPRT_ERROR, "Creation of the VD interface failed (%Rrc)", vrc);
907 break;
908 }
909 rc = readFSImpl(pTask, pszFilename, pShaIo, &storage);
910 }while(0);
911
912 RTTarClose(tar);
913
914 /* Cleanup */
915 if (pszFilename)
916 RTMemFree(pszFilename);
917 if (pShaIo)
918 RTMemFree(pShaIo);
919 if (pTarIo)
920 RTMemFree(pTarIo);
921
922 LogFlowFunc(("rc=%Rhrc\n", rc));
923 LogFlowFuncLeave();
924
925 return rc;
926}
927
928HRESULT Appliance::readFSImpl(TaskOVF *pTask, const RTCString &strFilename, PVDINTERFACEIO pIfIo, PSHASTORAGE pStorage)
929{
930 LogFlowFuncEnter();
931
932 HRESULT rc = S_OK;
933
934 pStorage->fCreateDigest = true;
935
936 void *pvTmpBuf = 0;
937 try
938 {
939 /* Read the OVF into a memory buffer */
940 size_t cbSize = 0;
941 int vrc = ShaReadBuf(strFilename.c_str(), &pvTmpBuf, &cbSize, pIfIo, pStorage);
942 if ( RT_FAILURE(vrc)
943 || !pvTmpBuf)
944 throw setError(VBOX_E_FILE_ERROR,
945 tr("Could not read OVF file '%s' (%Rrc)"),
946 RTPathFilename(strFilename.c_str()), vrc);
947 /* Copy the SHA1/SHA256 sum of the OVF file for later validation */
948 m->strOVFSHADigest = pStorage->strDigest;
949 /* Read & parse the XML structure of the OVF file */
950 m->pReader = new ovf::OVFReader(pvTmpBuf, cbSize, pTask->locInfo.strPath);
951 }
952 catch (RTCError &x) // includes all XML exceptions
953 {
954 rc = setError(VBOX_E_FILE_ERROR,
955 x.what());
956 }
957 catch (HRESULT aRC)
958 {
959 rc = aRC;
960 }
961
962 /* Cleanup */
963 if (pvTmpBuf)
964 RTMemFree(pvTmpBuf);
965
966 LogFlowFunc(("rc=%Rhrc\n", rc));
967 LogFlowFuncLeave();
968
969 return rc;
970}
971
972#ifdef VBOX_WITH_S3
973/**
974 * Worker code for reading OVF from the cloud. This is called from Appliance::taskThreadImportOrExport()
975 * in S3 mode and therefore runs on the OVF read worker thread. This then starts a second worker
976 * thread to create temporary files (see Appliance::readFS()).
977 *
978 * @param pTask
979 * @return
980 */
981HRESULT Appliance::readS3(TaskOVF *pTask)
982{
983 LogFlowFuncEnter();
984 LogFlowFunc(("Appliance %p\n", this));
985
986 AutoCaller autoCaller(this);
987 if (FAILED(autoCaller.rc())) return autoCaller.rc();
988
989 AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);
990
991 HRESULT rc = S_OK;
992 int vrc = VINF_SUCCESS;
993 RTS3 hS3 = NIL_RTS3;
994 char szOSTmpDir[RTPATH_MAX];
995 RTPathTemp(szOSTmpDir, sizeof(szOSTmpDir));
996 /* The template for the temporary directory created below */
997 char *pszTmpDir = RTPathJoinA(szOSTmpDir, "vbox-ovf-XXXXXX");
998 list< pair<Utf8Str, ULONG> > filesList;
999 Utf8Str strTmpOvf;
1000
1001 try
1002 {
1003 /* Extract the bucket */
1004 Utf8Str tmpPath = pTask->locInfo.strPath;
1005 Utf8Str bucket;
1006 parseBucket(tmpPath, bucket);
1007
1008 /* We need a temporary directory which we can put the OVF file & all
1009 * disk images in */
1010 vrc = RTDirCreateTemp(pszTmpDir);
1011 if (RT_FAILURE(vrc))
1012 throw setError(VBOX_E_FILE_ERROR,
1013 tr("Cannot create temporary directory '%s'"), pszTmpDir);
1014
1015 /* The temporary name of the target OVF file */
1016 strTmpOvf = Utf8StrFmt("%s/%s", pszTmpDir, RTPathFilename(tmpPath.c_str()));
1017
1018 /* Next we have to download the OVF */
1019 vrc = RTS3Create(&hS3, pTask->locInfo.strUsername.c_str(), pTask->locInfo.strPassword.c_str(), pTask->locInfo.strHostname.c_str(), "virtualbox-agent/"VBOX_VERSION_STRING);
1020 if (RT_FAILURE(vrc))
1021 throw setError(VBOX_E_IPRT_ERROR,
1022 tr("Cannot create S3 service handler"));
1023 RTS3SetProgressCallback(hS3, pTask->updateProgress, &pTask);
1024
1025 /* Get it */
1026 char *pszFilename = RTPathFilename(strTmpOvf.c_str());
1027 vrc = RTS3GetKey(hS3, bucket.c_str(), pszFilename, strTmpOvf.c_str());
1028 if (RT_FAILURE(vrc))
1029 {
1030 if (vrc == VERR_S3_CANCELED)
1031 throw S_OK; /* todo: !!!!!!!!!!!!! */
1032 else if (vrc == VERR_S3_ACCESS_DENIED)
1033 throw setError(E_ACCESSDENIED,
1034 tr("Cannot download file '%s' from S3 storage server (Access denied). Make sure that your credentials are right."
1035 "Also check that your host clock is properly synced"),
1036 pszFilename);
1037 else if (vrc == VERR_S3_NOT_FOUND)
1038 throw setError(VBOX_E_FILE_ERROR,
1039 tr("Cannot download file '%s' from S3 storage server (File not found)"), pszFilename);
1040 else
1041 throw setError(VBOX_E_IPRT_ERROR,
1042 tr("Cannot download file '%s' from S3 storage server (%Rrc)"), pszFilename, vrc);
1043 }
1044
1045 /* Close the connection early */
1046 RTS3Destroy(hS3);
1047 hS3 = NIL_RTS3;
1048
1049 pTask->pProgress->SetNextOperation(Bstr(tr("Reading")).raw(), 1);
1050
1051 /* Prepare the temporary reading of the OVF */
1052 ComObjPtr<Progress> progress;
1053 LocationInfo li;
1054 li.strPath = strTmpOvf;
1055 /* Start the reading from the fs */
1056 rc = readImpl(li, progress);
1057 if (FAILED(rc)) throw rc;
1058
1059 /* Unlock the appliance for the reading thread */
1060 appLock.release();
1061 /* Wait until the reading is done, but report the progress back to the
1062 caller */
1063 ComPtr<IProgress> progressInt(progress);
1064 waitForAsyncProgress(pTask->pProgress, progressInt); /* Any errors will be thrown */
1065
1066 /* Again lock the appliance for the next steps */
1067 appLock.acquire();
1068 }
1069 catch(HRESULT aRC)
1070 {
1071 rc = aRC;
1072 }
1073 /* Cleanup */
1074 RTS3Destroy(hS3);
1075 /* Delete all files which where temporary created */
1076 if (RTPathExists(strTmpOvf.c_str()))
1077 {
1078 vrc = RTFileDelete(strTmpOvf.c_str());
1079 if (RT_FAILURE(vrc))
1080 rc = setError(VBOX_E_FILE_ERROR,
1081 tr("Cannot delete file '%s' (%Rrc)"), strTmpOvf.c_str(), vrc);
1082 }
1083 /* Delete the temporary directory */
1084 if (RTPathExists(pszTmpDir))
1085 {
1086 vrc = RTDirRemove(pszTmpDir);
1087 if (RT_FAILURE(vrc))
1088 rc = setError(VBOX_E_FILE_ERROR,
1089 tr("Cannot delete temporary directory '%s' (%Rrc)"), pszTmpDir, vrc);
1090 }
1091 if (pszTmpDir)
1092 RTStrFree(pszTmpDir);
1093
1094 LogFlowFunc(("rc=%Rhrc\n", rc));
1095 LogFlowFuncLeave();
1096
1097 return rc;
1098}
1099#endif /* VBOX_WITH_S3 */
1100
1101/*******************************************************************************
1102 * Import stuff
1103 ******************************************************************************/
1104
1105/**
1106 * Implementation for importing OVF data into VirtualBox. This starts a new thread which will call
1107 * Appliance::taskThreadImportOrExport().
1108 *
1109 * This creates one or more new machines according to the VirtualSystemScription instances created by
1110 * Appliance::Interpret().
1111 *
1112 * This is in a separate private method because it is used from two locations:
1113 *
1114 * 1) from the public Appliance::ImportMachines().
1115 * 2) from Appliance::importS3(), which got called from a previous instance of Appliance::taskThreadImportOrExport().
1116 *
1117 * @param aLocInfo
1118 * @param aProgress
1119 * @return
1120 */
1121HRESULT Appliance::importImpl(const LocationInfo &locInfo,
1122 ComObjPtr<Progress> &progress)
1123{
1124 HRESULT rc = S_OK;
1125
1126 SetUpProgressMode mode;
1127 if (locInfo.storageType == VFSType_File)
1128 mode = ImportFile;
1129 else
1130 mode = ImportS3;
1131
1132 rc = setUpProgress(progress,
1133 BstrFmt(tr("Importing appliance '%s'"), locInfo.strPath.c_str()),
1134 mode);
1135 if (FAILED(rc)) throw rc;
1136
1137 /* Initialize our worker task */
1138 std::auto_ptr<TaskOVF> task(new TaskOVF(this, TaskOVF::Import, locInfo, progress));
1139
1140 rc = task->startThread();
1141 if (FAILED(rc)) throw rc;
1142
1143 /* Don't destruct on success */
1144 task.release();
1145
1146 return rc;
1147}
1148
1149/**
1150 * Actual worker code for importing OVF data into VirtualBox. This is called from Appliance::taskThreadImportOrExport()
1151 * and therefore runs on the OVF import worker thread. This creates one or more new machines according to the
1152 * VirtualSystemScription instances created by Appliance::Interpret().
1153 *
1154 * This runs in three contexts:
1155 *
1156 * 1) in a first worker thread; in that case, Appliance::ImportMachines() called Appliance::importImpl();
1157 *
1158 * 2) in a second worker thread; in that case, Appliance::ImportMachines() called Appliance::importImpl(), which
1159 * called Appliance::importFSOVA(), which called Appliance::importImpl(), which then called this again.
1160 *
1161 * 3) in a second worker thread; in that case, Appliance::ImportMachines() called Appliance::importImpl(), which
1162 * called Appliance::importS3(), which called Appliance::importImpl(), which then called this again.
1163 *
1164 * @param pTask
1165 * @return
1166 */
1167HRESULT Appliance::importFS(TaskOVF *pTask)
1168{
1169
1170 LogFlowFuncEnter();
1171 LogFlowFunc(("Appliance %p\n", this));
1172
1173 AutoCaller autoCaller(this);
1174 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1175
1176 /* Change the appliance state so we can safely leave the lock while doing
1177 * time-consuming disk imports; also the below method calls do all kinds of
1178 * locking which conflicts with the appliance object lock. */
1179 AutoWriteLock writeLock(this COMMA_LOCKVAL_SRC_POS);
1180 /* Check if the appliance is currently busy. */
1181 if (!isApplianceIdle())
1182 return E_ACCESSDENIED;
1183 /* Set the internal state to importing. */
1184 m->state = Data::ApplianceImporting;
1185
1186 HRESULT rc = S_OK;
1187
1188 /* Clear the list of imported machines, if any */
1189 m->llGuidsMachinesCreated.clear();
1190
1191 if (pTask->locInfo.strPath.endsWith(".ovf", Utf8Str::CaseInsensitive))
1192 rc = importFSOVF(pTask, writeLock);
1193 else
1194 rc = importFSOVA(pTask, writeLock);
1195
1196 if (FAILED(rc))
1197 {
1198 /* With _whatever_ error we've had, do a complete roll-back of
1199 * machines and disks we've created */
1200 writeLock.release();
1201 for (list<Guid>::iterator itID = m->llGuidsMachinesCreated.begin();
1202 itID != m->llGuidsMachinesCreated.end();
1203 ++itID)
1204 {
1205 Guid guid = *itID;
1206 Bstr bstrGuid = guid.toUtf16();
1207 ComPtr<IMachine> failedMachine;
1208 HRESULT rc2 = mVirtualBox->FindMachine(bstrGuid.raw(), failedMachine.asOutParam());
1209 if (SUCCEEDED(rc2))
1210 {
1211 SafeIfaceArray<IMedium> aMedia;
1212 rc2 = failedMachine->Unregister(CleanupMode_DetachAllReturnHardDisksOnly, ComSafeArrayAsOutParam(aMedia));
1213 ComPtr<IProgress> pProgress2;
1214 rc2 = failedMachine->Delete(ComSafeArrayAsInParam(aMedia), pProgress2.asOutParam());
1215 pProgress2->WaitForCompletion(-1);
1216 }
1217 }
1218 writeLock.acquire();
1219 }
1220
1221 /* Reset the state so others can call methods again */
1222 m->state = Data::ApplianceIdle;
1223
1224 LogFlowFunc(("rc=%Rhrc\n", rc));
1225 LogFlowFuncLeave();
1226
1227 return rc;
1228}
1229
1230HRESULT Appliance::importFSOVF(TaskOVF *pTask, AutoWriteLockBase& writeLock)
1231{
1232 LogFlowFuncEnter();
1233
1234 HRESULT rc = S_OK;
1235
1236 PVDINTERFACEIO pShaIo = NULL;
1237 PVDINTERFACEIO pFileIo = NULL;
1238 void *pvMfBuf = NULL;
1239 writeLock.release();
1240 try
1241 {
1242 /* Create the necessary file access interfaces. */
1243 pFileIo = FileCreateInterface();
1244 if (!pFileIo)
1245 throw setError(E_OUTOFMEMORY);
1246
1247 Utf8Str strMfFile = Utf8Str(pTask->locInfo.strPath).stripExt().append(".mf");
1248 /* Create the import stack for the rollback on errors. */
1249 ImportStack stack(pTask->locInfo, m->pReader->m_mapDisks, pTask->pProgress);
1250
1251 if (RTFileExists(strMfFile.c_str()))
1252 {
1253 SHASTORAGE storage;
1254 RT_ZERO(storage);
1255
1256 pShaIo = ShaCreateInterface();
1257 if (!pShaIo)
1258 throw setError(E_OUTOFMEMORY);
1259
1260 storage.fCreateDigest = true;
1261 int vrc = VDInterfaceAdd(&pFileIo->Core, "Appliance::IOFile",
1262 VDINTERFACETYPE_IO, 0, sizeof(VDINTERFACEIO),
1263 &storage.pVDImageIfaces);
1264 if (RT_FAILURE(vrc))
1265 throw setError(VBOX_E_IPRT_ERROR, "Creation of the VD interface failed (%Rrc)", vrc);
1266
1267 size_t cbMfSize = 0;
1268 storage.fCreateDigest = true;
1269 /* Now import the appliance. */
1270 importMachines(stack, pShaIo, &storage);
1271 /* Read & verify the manifest file. */
1272 /* Add the ovf file to the digest list. */
1273 stack.llSrcDisksDigest.push_front(STRPAIR(pTask->locInfo.strPath, m->strOVFSHADigest));
1274 rc = readManifestFile(strMfFile, &pvMfBuf, &cbMfSize, pShaIo, &storage);
1275 if (FAILED(rc)) throw rc;
1276 rc = verifyManifestFile(strMfFile, stack, pvMfBuf, cbMfSize);
1277 if (FAILED(rc)) throw rc;
1278 }
1279 else
1280 importMachines(stack, pFileIo, NULL);
1281 }
1282 catch (HRESULT rc2)
1283 {
1284 rc = rc2;
1285 }
1286 writeLock.acquire();
1287
1288 /* Cleanup */
1289 if (pvMfBuf)
1290 RTMemFree(pvMfBuf);
1291 if (pShaIo)
1292 RTMemFree(pShaIo);
1293 if (pFileIo)
1294 RTMemFree(pFileIo);
1295
1296 LogFlowFunc(("rc=%Rhrc\n", rc));
1297 LogFlowFuncLeave();
1298
1299 return rc;
1300}
1301
1302HRESULT Appliance::importFSOVA(TaskOVF *pTask, AutoWriteLockBase& writeLock)
1303{
1304 LogFlowFuncEnter();
1305
1306 RTTAR tar;
1307 int vrc = RTTarOpen(&tar, pTask->locInfo.strPath.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, true);
1308 if (RT_FAILURE(vrc))
1309 return setError(VBOX_E_FILE_ERROR,
1310 tr("Could not open OVA file '%s' (%Rrc)"),
1311 pTask->locInfo.strPath.c_str(), vrc);
1312
1313 HRESULT rc = S_OK;
1314
1315 PVDINTERFACEIO pShaIo = 0;
1316 PVDINTERFACEIO pTarIo = 0;
1317 char *pszFilename = 0;
1318 void *pvMfBuf = 0;
1319 writeLock.release();
1320 try
1321 {
1322 /* Create the necessary file access interfaces. */
1323 pShaIo = ShaCreateInterface();
1324 if (!pShaIo)
1325 throw setError(E_OUTOFMEMORY);
1326 pTarIo = TarCreateInterface();
1327 if (!pTarIo)
1328 throw setError(E_OUTOFMEMORY);
1329
1330 SHASTORAGE storage;
1331 RT_ZERO(storage);
1332 vrc = VDInterfaceAdd(&pTarIo->Core, "Appliance::IOTar",
1333 VDINTERFACETYPE_IO, tar, sizeof(VDINTERFACEIO),
1334 &storage.pVDImageIfaces);
1335 if (RT_FAILURE(vrc))
1336 throw setError(VBOX_E_IPRT_ERROR,
1337 tr("Creation of the VD interface failed (%Rrc)"), vrc);
1338
1339 /* Read the file name of the first file (need to be the ovf file). This
1340 * is how all internal files are named. */
1341 vrc = RTTarCurrentFile(tar, &pszFilename);
1342 if (RT_FAILURE(vrc))
1343 throw setError(VBOX_E_IPRT_ERROR,
1344 tr("Getting the current file within the archive failed (%Rrc)"), vrc);
1345 /* Skip the OVF file, cause this was read in IAppliance::Read already. */
1346 vrc = RTTarSeekNextFile(tar);
1347 if ( RT_FAILURE(vrc)
1348 && vrc != VERR_TAR_END_OF_FILE)
1349 throw setError(VBOX_E_IPRT_ERROR,
1350 tr("Seeking within the archive failed (%Rrc)"), vrc);
1351
1352 PVDINTERFACEIO pCallbacks = pShaIo;
1353 PSHASTORAGE pStorage = &storage;
1354
1355 /* We always need to create the digest, cause we didn't know if there
1356 * is a manifest file in the stream. */
1357 pStorage->fCreateDigest = true;
1358
1359 size_t cbMfSize = 0;
1360 Utf8Str strMfFile = Utf8Str(pszFilename).stripExt().append(".mf");
1361 /* Create the import stack for the rollback on errors. */
1362 ImportStack stack(pTask->locInfo, m->pReader->m_mapDisks, pTask->pProgress);
1363 /*
1364 * Try to read the manifest file. First try.
1365 *
1366 * Note: This isn't fatal if the file is not found. The standard
1367 * defines 3 cases.
1368 * 1. no manifest file
1369 * 2. manifest file after the OVF file
1370 * 3. manifest file after all disk files
1371 * If we want streaming capabilities, we can't check if it is there by
1372 * searching for it. We have to try to open it on all possible places.
1373 * If it fails here, we will try it again after all disks where read.
1374 */
1375 rc = readTarManifestFile(tar, strMfFile, &pvMfBuf, &cbMfSize, pCallbacks, pStorage);
1376 if (FAILED(rc)) throw rc;
1377 /* Now import the appliance. */
1378 importMachines(stack, pCallbacks, pStorage);
1379 /* Try to read the manifest file. Second try. */
1380 if (!pvMfBuf)
1381 {
1382 rc = readTarManifestFile(tar, strMfFile, &pvMfBuf, &cbMfSize, pCallbacks, pStorage);
1383 if (FAILED(rc)) throw rc;
1384 }
1385 /* If we were able to read a manifest file we can check it now. */
1386 if (pvMfBuf)
1387 {
1388 /* Add the ovf file to the digest list. */
1389 stack.llSrcDisksDigest.push_front(STRPAIR(Utf8Str(pszFilename).stripExt().append(".ovf"), m->strOVFSHADigest));
1390 rc = verifyManifestFile(strMfFile, stack, pvMfBuf, cbMfSize);
1391 if (FAILED(rc)) throw rc;
1392 }
1393 }
1394 catch (HRESULT rc2)
1395 {
1396 rc = rc2;
1397 }
1398 writeLock.acquire();
1399
1400 RTTarClose(tar);
1401
1402 /* Cleanup */
1403 if (pszFilename)
1404 RTMemFree(pszFilename);
1405 if (pvMfBuf)
1406 RTMemFree(pvMfBuf);
1407 if (pShaIo)
1408 RTMemFree(pShaIo);
1409 if (pTarIo)
1410 RTMemFree(pTarIo);
1411
1412 LogFlowFunc(("rc=%Rhrc\n", rc));
1413 LogFlowFuncLeave();
1414
1415 return rc;
1416}
1417
1418#ifdef VBOX_WITH_S3
1419/**
1420 * Worker code for importing OVF from the cloud. This is called from Appliance::taskThreadImportOrExport()
1421 * in S3 mode and therefore runs on the OVF import worker thread. This then starts a second worker
1422 * thread to import from temporary files (see Appliance::importFS()).
1423 * @param pTask
1424 * @return
1425 */
1426HRESULT Appliance::importS3(TaskOVF *pTask)
1427{
1428 LogFlowFuncEnter();
1429 LogFlowFunc(("Appliance %p\n", this));
1430
1431 AutoCaller autoCaller(this);
1432 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1433
1434 AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);
1435
1436 int vrc = VINF_SUCCESS;
1437 RTS3 hS3 = NIL_RTS3;
1438 char szOSTmpDir[RTPATH_MAX];
1439 RTPathTemp(szOSTmpDir, sizeof(szOSTmpDir));
1440 /* The template for the temporary directory created below */
1441 char *pszTmpDir = RTPathJoinA(szOSTmpDir, "vbox-ovf-XXXXXX");
1442 list< pair<Utf8Str, ULONG> > filesList;
1443
1444 HRESULT rc = S_OK;
1445 try
1446 {
1447 /* Extract the bucket */
1448 Utf8Str tmpPath = pTask->locInfo.strPath;
1449 Utf8Str bucket;
1450 parseBucket(tmpPath, bucket);
1451
1452 /* We need a temporary directory which we can put the all disk images
1453 * in */
1454 vrc = RTDirCreateTemp(pszTmpDir);
1455 if (RT_FAILURE(vrc))
1456 throw setError(VBOX_E_FILE_ERROR,
1457 tr("Cannot create temporary directory '%s' (%Rrc)"), pszTmpDir, vrc);
1458
1459 /* Add every disks of every virtual system to an internal list */
1460 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;
1461 for (it = m->virtualSystemDescriptions.begin();
1462 it != m->virtualSystemDescriptions.end();
1463 ++it)
1464 {
1465 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);
1466 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);
1467 std::list<VirtualSystemDescriptionEntry*>::const_iterator itH;
1468 for (itH = avsdeHDs.begin();
1469 itH != avsdeHDs.end();
1470 ++itH)
1471 {
1472 const Utf8Str &strTargetFile = (*itH)->strOvf;
1473 if (!strTargetFile.isEmpty())
1474 {
1475 /* The temporary name of the target disk file */
1476 Utf8StrFmt strTmpDisk("%s/%s", pszTmpDir, RTPathFilename(strTargetFile.c_str()));
1477 filesList.push_back(pair<Utf8Str, ULONG>(strTmpDisk, (*itH)->ulSizeMB));
1478 }
1479 }
1480 }
1481
1482 /* Next we have to download the disk images */
1483 vrc = RTS3Create(&hS3, pTask->locInfo.strUsername.c_str(), pTask->locInfo.strPassword.c_str(), pTask->locInfo.strHostname.c_str(), "virtualbox-agent/"VBOX_VERSION_STRING);
1484 if (RT_FAILURE(vrc))
1485 throw setError(VBOX_E_IPRT_ERROR,
1486 tr("Cannot create S3 service handler"));
1487 RTS3SetProgressCallback(hS3, pTask->updateProgress, &pTask);
1488
1489 /* Download all files */
1490 for (list< pair<Utf8Str, ULONG> >::const_iterator it1 = filesList.begin(); it1 != filesList.end(); ++it1)
1491 {
1492 const pair<Utf8Str, ULONG> &s = (*it1);
1493 const Utf8Str &strSrcFile = s.first;
1494 /* Construct the source file name */
1495 char *pszFilename = RTPathFilename(strSrcFile.c_str());
1496 /* Advance to the next operation */
1497 if (!pTask->pProgress.isNull())
1498 pTask->pProgress->SetNextOperation(BstrFmt(tr("Downloading file '%s'"), pszFilename).raw(), s.second);
1499
1500 vrc = RTS3GetKey(hS3, bucket.c_str(), pszFilename, strSrcFile.c_str());
1501 if (RT_FAILURE(vrc))
1502 {
1503 if (vrc == VERR_S3_CANCELED)
1504 throw S_OK; /* todo: !!!!!!!!!!!!! */
1505 else if (vrc == VERR_S3_ACCESS_DENIED)
1506 throw setError(E_ACCESSDENIED,
1507 tr("Cannot download file '%s' from S3 storage server (Access denied). "
1508 "Make sure that your credentials are right. Also check that your host clock is properly synced"),
1509 pszFilename);
1510 else if (vrc == VERR_S3_NOT_FOUND)
1511 throw setError(VBOX_E_FILE_ERROR,
1512 tr("Cannot download file '%s' from S3 storage server (File not found)"),
1513 pszFilename);
1514 else
1515 throw setError(VBOX_E_IPRT_ERROR,
1516 tr("Cannot download file '%s' from S3 storage server (%Rrc)"),
1517 pszFilename, vrc);
1518 }
1519 }
1520
1521 /* Provide a OVF file (haven't to exist) so the import routine can
1522 * figure out where the disk images/manifest file are located. */
1523 Utf8StrFmt strTmpOvf("%s/%s", pszTmpDir, RTPathFilename(tmpPath.c_str()));
1524 /* Now check if there is an manifest file. This is optional. */
1525 Utf8Str strManifestFile; //= queryManifestFileName(strTmpOvf);
1526// Utf8Str strManifestFile = queryManifestFileName(strTmpOvf);
1527 char *pszFilename = RTPathFilename(strManifestFile.c_str());
1528 if (!pTask->pProgress.isNull())
1529 pTask->pProgress->SetNextOperation(BstrFmt(tr("Downloading file '%s'"), pszFilename).raw(), 1);
1530
1531 /* Try to download it. If the error is VERR_S3_NOT_FOUND, it isn't fatal. */
1532 vrc = RTS3GetKey(hS3, bucket.c_str(), pszFilename, strManifestFile.c_str());
1533 if (RT_SUCCESS(vrc))
1534 filesList.push_back(pair<Utf8Str, ULONG>(strManifestFile, 0));
1535 else if (RT_FAILURE(vrc))
1536 {
1537 if (vrc == VERR_S3_CANCELED)
1538 throw S_OK; /* todo: !!!!!!!!!!!!! */
1539 else if (vrc == VERR_S3_NOT_FOUND)
1540 vrc = VINF_SUCCESS; /* Not found is ok */
1541 else if (vrc == VERR_S3_ACCESS_DENIED)
1542 throw setError(E_ACCESSDENIED,
1543 tr("Cannot download file '%s' from S3 storage server (Access denied)."
1544 "Make sure that your credentials are right. Also check that your host clock is properly synced"),
1545 pszFilename);
1546 else
1547 throw setError(VBOX_E_IPRT_ERROR,
1548 tr("Cannot download file '%s' from S3 storage server (%Rrc)"),
1549 pszFilename, vrc);
1550 }
1551
1552 /* Close the connection early */
1553 RTS3Destroy(hS3);
1554 hS3 = NIL_RTS3;
1555
1556 pTask->pProgress->SetNextOperation(BstrFmt(tr("Importing appliance")).raw(), m->ulWeightForXmlOperation);
1557
1558 ComObjPtr<Progress> progress;
1559 /* Import the whole temporary OVF & the disk images */
1560 LocationInfo li;
1561 li.strPath = strTmpOvf;
1562 rc = importImpl(li, progress);
1563 if (FAILED(rc)) throw rc;
1564
1565 /* Unlock the appliance for the fs import thread */
1566 appLock.release();
1567 /* Wait until the import is done, but report the progress back to the
1568 caller */
1569 ComPtr<IProgress> progressInt(progress);
1570 waitForAsyncProgress(pTask->pProgress, progressInt); /* Any errors will be thrown */
1571
1572 /* Again lock the appliance for the next steps */
1573 appLock.acquire();
1574 }
1575 catch(HRESULT aRC)
1576 {
1577 rc = aRC;
1578 }
1579 /* Cleanup */
1580 RTS3Destroy(hS3);
1581 /* Delete all files which where temporary created */
1582 for (list< pair<Utf8Str, ULONG> >::const_iterator it1 = filesList.begin(); it1 != filesList.end(); ++it1)
1583 {
1584 const char *pszFilePath = (*it1).first.c_str();
1585 if (RTPathExists(pszFilePath))
1586 {
1587 vrc = RTFileDelete(pszFilePath);
1588 if (RT_FAILURE(vrc))
1589 rc = setError(VBOX_E_FILE_ERROR,
1590 tr("Cannot delete file '%s' (%Rrc)"), pszFilePath, vrc);
1591 }
1592 }
1593 /* Delete the temporary directory */
1594 if (RTPathExists(pszTmpDir))
1595 {
1596 vrc = RTDirRemove(pszTmpDir);
1597 if (RT_FAILURE(vrc))
1598 rc = setError(VBOX_E_FILE_ERROR,
1599 tr("Cannot delete temporary directory '%s' (%Rrc)"), pszTmpDir, vrc);
1600 }
1601 if (pszTmpDir)
1602 RTStrFree(pszTmpDir);
1603
1604 LogFlowFunc(("rc=%Rhrc\n", rc));
1605 LogFlowFuncLeave();
1606
1607 return rc;
1608}
1609#endif /* VBOX_WITH_S3 */
1610
1611HRESULT Appliance::readManifestFile(const Utf8Str &strFile, void **ppvBuf, size_t *pcbSize, PVDINTERFACEIO pCallbacks, PSHASTORAGE pStorage)
1612{
1613 HRESULT rc = S_OK;
1614
1615 bool fOldDigest = pStorage->fCreateDigest;
1616 pStorage->fCreateDigest = false; /* No digest for the manifest file */
1617 int vrc = ShaReadBuf(strFile.c_str(), ppvBuf, pcbSize, pCallbacks, pStorage);
1618 if ( RT_FAILURE(vrc)
1619 && vrc != VERR_FILE_NOT_FOUND)
1620 rc = setError(VBOX_E_FILE_ERROR,
1621 tr("Could not read manifest file '%s' (%Rrc)"),
1622 RTPathFilename(strFile.c_str()), vrc);
1623 pStorage->fCreateDigest = fOldDigest; /* Restore the old digest creation behavior again. */
1624
1625 return rc;
1626}
1627
1628HRESULT Appliance::readTarManifestFile(RTTAR tar, const Utf8Str &strFile, void **ppvBuf, size_t *pcbSize, PVDINTERFACEIO pCallbacks, PSHASTORAGE pStorage)
1629{
1630 HRESULT rc = S_OK;
1631
1632 char *pszCurFile;
1633 int vrc = RTTarCurrentFile(tar, &pszCurFile);
1634 if (RT_SUCCESS(vrc))
1635 {
1636 if (!strcmp(pszCurFile, RTPathFilename(strFile.c_str())))
1637 rc = readManifestFile(strFile, ppvBuf, pcbSize, pCallbacks, pStorage);
1638 RTStrFree(pszCurFile);
1639 }
1640 else if (vrc != VERR_TAR_END_OF_FILE)
1641 rc = setError(VBOX_E_IPRT_ERROR, "Seeking within the archive failed (%Rrc)", vrc);
1642
1643 return rc;
1644}
1645
1646HRESULT Appliance::verifyManifestFile(const Utf8Str &strFile, ImportStack &stack, void *pvBuf, size_t cbSize)
1647{
1648 HRESULT rc = S_OK;
1649
1650 PRTMANIFESTTEST paTests = (PRTMANIFESTTEST)RTMemAlloc(sizeof(RTMANIFESTTEST) * stack.llSrcDisksDigest.size());
1651 if (!paTests)
1652 return E_OUTOFMEMORY;
1653
1654 size_t i = 0;
1655 list<STRPAIR>::const_iterator it1;
1656 for (it1 = stack.llSrcDisksDigest.begin();
1657 it1 != stack.llSrcDisksDigest.end();
1658 ++it1, ++i)
1659 {
1660 paTests[i].pszTestFile = (*it1).first.c_str();
1661 paTests[i].pszTestDigest = (*it1).second.c_str();
1662 }
1663 size_t iFailed;
1664 int vrc = RTManifestVerifyFilesBuf(pvBuf, cbSize, paTests, stack.llSrcDisksDigest.size(), &iFailed);
1665 if (RT_UNLIKELY(vrc == VERR_MANIFEST_DIGEST_MISMATCH))
1666 rc = setError(VBOX_E_FILE_ERROR,
1667 tr("The SHA1 digest of '%s' does not match the one in '%s' (%Rrc)"),
1668 RTPathFilename(paTests[iFailed].pszTestFile), RTPathFilename(strFile.c_str()), vrc);
1669 else if (RT_FAILURE(vrc))
1670 rc = setError(VBOX_E_FILE_ERROR,
1671 tr("Could not verify the content of '%s' against the available files (%Rrc)"),
1672 RTPathFilename(strFile.c_str()), vrc);
1673
1674 RTMemFree(paTests);
1675
1676 return rc;
1677}
1678
1679
1680/**
1681 * Helper that converts VirtualSystem attachment values into VirtualBox attachment values.
1682 * Throws HRESULT values on errors!
1683 *
1684 * @param hdc in: the HardDiskController structure to attach to.
1685 * @param ulAddressOnParent in: the AddressOnParent parameter from OVF.
1686 * @param controllerType out: the name of the hard disk controller to attach to (e.g. "IDE Controller").
1687 * @param lControllerPort out: the channel (controller port) of the controller to attach to.
1688 * @param lDevice out: the device number to attach to.
1689 */
1690void Appliance::convertDiskAttachmentValues(const ovf::HardDiskController &hdc,
1691 uint32_t ulAddressOnParent,
1692 Bstr &controllerType,
1693 int32_t &lControllerPort,
1694 int32_t &lDevice)
1695{
1696 Log(("Appliance::convertDiskAttachmentValues: hdc.system=%d, hdc.fPrimary=%d, ulAddressOnParent=%d\n", hdc.system, hdc.fPrimary, ulAddressOnParent));
1697
1698 switch (hdc.system)
1699 {
1700 case ovf::HardDiskController::IDE:
1701 // For the IDE bus, the port parameter can be either 0 or 1, to specify the primary
1702 // or secondary IDE controller, respectively. For the primary controller of the IDE bus,
1703 // the device number can be either 0 or 1, to specify the master or the slave device,
1704 // respectively. For the secondary IDE controller, the device number is always 1 because
1705 // the master device is reserved for the CD-ROM drive.
1706 controllerType = Bstr("IDE Controller");
1707 switch (ulAddressOnParent)
1708 {
1709 case 0: // master
1710 if (!hdc.fPrimary)
1711 {
1712 // secondary master
1713 lControllerPort = (long)1;
1714 lDevice = (long)0;
1715 }
1716 else // primary master
1717 {
1718 lControllerPort = (long)0;
1719 lDevice = (long)0;
1720 }
1721 break;
1722
1723 case 1: // slave
1724 if (!hdc.fPrimary)
1725 {
1726 // secondary slave
1727 lControllerPort = (long)1;
1728 lDevice = (long)1;
1729 }
1730 else // primary slave
1731 {
1732 lControllerPort = (long)0;
1733 lDevice = (long)1;
1734 }
1735 break;
1736
1737 // used by older VBox exports
1738 case 2: // interpret this as secondary master
1739 lControllerPort = (long)1;
1740 lDevice = (long)0;
1741 break;
1742
1743 // used by older VBox exports
1744 case 3: // interpret this as secondary slave
1745 lControllerPort = (long)1;
1746 lDevice = (long)1;
1747 break;
1748
1749 default:
1750 throw setError(VBOX_E_NOT_SUPPORTED,
1751 tr("Invalid channel %RI16 specified; IDE controllers support only 0, 1 or 2"),
1752 ulAddressOnParent);
1753 break;
1754 }
1755 break;
1756
1757 case ovf::HardDiskController::SATA:
1758 controllerType = Bstr("SATA Controller");
1759 lControllerPort = (long)ulAddressOnParent;
1760 lDevice = (long)0;
1761 break;
1762
1763 case ovf::HardDiskController::SCSI:
1764 controllerType = Bstr("SCSI Controller");
1765 lControllerPort = (long)ulAddressOnParent;
1766 lDevice = (long)0;
1767 break;
1768
1769 default: break;
1770 }
1771
1772 Log(("=> lControllerPort=%d, lDevice=%d\n", lControllerPort, lDevice));
1773}
1774
1775/**
1776 * Imports one disk image. This is common code shared between
1777 * -- importMachineGeneric() for the OVF case; in that case the information comes from
1778 * the OVF virtual systems;
1779 * -- importVBoxMachine(); in that case, the information comes from the <vbox:Machine>
1780 * tag.
1781 *
1782 * Both ways of describing machines use the OVF disk references section, so in both cases
1783 * the caller needs to pass in the ovf::DiskImage structure from ovfreader.cpp.
1784 *
1785 * As a result, in both cases, if di.strHref is empty, we create a new disk as per the OVF
1786 * spec, even though this cannot really happen in the vbox:Machine case since such data
1787 * would never have been exported.
1788 *
1789 * This advances stack.pProgress by one operation with the disk's weight.
1790 *
1791 * @param di ovfreader.cpp structure describing the disk image from the OVF that is to be imported
1792 * @param ulSizeMB Size of the disk image (for progress reporting)
1793 * @param strTargetPath Where to create the target image.
1794 * @param pTargetHD out: The newly created target disk. This also gets pushed on stack.llHardDisksCreated for cleanup.
1795 * @param stack
1796 */
1797void Appliance::importOneDiskImage(const ovf::DiskImage &di,
1798 const Utf8Str &strTargetPath,
1799 ComObjPtr<Medium> &pTargetHD,
1800 ImportStack &stack,
1801 PVDINTERFACEIO pCallbacks,
1802 PSHASTORAGE pStorage)
1803{
1804 ComObjPtr<Progress> pProgress;
1805 pProgress.createObject();
1806 HRESULT rc = pProgress->init(mVirtualBox, static_cast<IAppliance*>(this), BstrFmt(tr("Creating medium '%s'"), strTargetPath.c_str()).raw(), TRUE);
1807 if (FAILED(rc)) throw rc;
1808
1809 /* Get the system properties. */
1810 SystemProperties *pSysProps = mVirtualBox->getSystemProperties();
1811
1812 /* First of all check if the path is an UUID. If so, the user like to
1813 * import the disk into an existing path. This is useful for iSCSI for
1814 * example. */
1815 RTUUID uuid;
1816 int vrc = RTUuidFromStr(&uuid, strTargetPath.c_str());
1817 if (vrc == VINF_SUCCESS)
1818 {
1819 rc = mVirtualBox->findHardDiskById(Guid(uuid), true, &pTargetHD);
1820 if (FAILED(rc)) throw rc;
1821 }
1822 else
1823 {
1824 Utf8Str strTrgFormat = "VMDK";
1825 if (RTPathHaveExt(strTargetPath.c_str()))
1826 {
1827 char *pszExt = RTPathExt(strTargetPath.c_str());
1828 /* Figure out which format the user like to have. Default is VMDK. */
1829 ComObjPtr<MediumFormat> trgFormat = pSysProps->mediumFormatFromExtension(&pszExt[1]);
1830 if (trgFormat.isNull())
1831 throw setError(VBOX_E_NOT_SUPPORTED,
1832 tr("Could not find a valid medium format for the target disk '%s'"),
1833 strTargetPath.c_str());
1834 /* Check the capabilities. We need create capabilities. */
1835 ULONG lCabs = 0;
1836 rc = trgFormat->COMGETTER(Capabilities)(&lCabs);
1837 if (FAILED(rc)) throw rc;
1838 if (!( ((lCabs & MediumFormatCapabilities_CreateFixed) == MediumFormatCapabilities_CreateFixed)
1839 || ((lCabs & MediumFormatCapabilities_CreateDynamic) == MediumFormatCapabilities_CreateDynamic)))
1840 throw setError(VBOX_E_NOT_SUPPORTED,
1841 tr("Could not find a valid medium format for the target disk '%s'"),
1842 strTargetPath.c_str());
1843 Bstr bstrFormatName;
1844 rc = trgFormat->COMGETTER(Name)(bstrFormatName.asOutParam());
1845 if (FAILED(rc)) throw rc;
1846 strTrgFormat = Utf8Str(bstrFormatName);
1847 }
1848
1849 /* Create an IMedium object. */
1850 pTargetHD.createObject();
1851 rc = pTargetHD->init(mVirtualBox,
1852 strTrgFormat,
1853 strTargetPath,
1854 Guid::Empty /* media registry: none yet */);
1855 if (FAILED(rc)) throw rc;
1856
1857 /* Now create an empty hard disk. */
1858 rc = mVirtualBox->CreateHardDisk(NULL,
1859 Bstr(strTargetPath).raw(),
1860 ComPtr<IMedium>(pTargetHD).asOutParam());
1861 if (FAILED(rc)) throw rc;
1862 }
1863
1864 const Utf8Str &strSourceOVF = di.strHref;
1865 /* Construct source file path */
1866 Utf8StrFmt strSrcFilePath("%s%c%s", stack.strSourceDir.c_str(), RTPATH_DELIMITER, strSourceOVF.c_str());
1867
1868 /* If strHref is empty we have to create a new file. */
1869 if (strSourceOVF.isEmpty())
1870 {
1871 /* Create a dynamic growing disk image with the given capacity. */
1872 rc = pTargetHD->CreateBaseStorage(di.iCapacity / _1M, MediumVariant_Standard, ComPtr<IProgress>(pProgress).asOutParam());
1873 if (FAILED(rc)) throw rc;
1874
1875 /* Advance to the next operation. */
1876 stack.pProgress->SetNextOperation(BstrFmt(tr("Creating disk image '%s'"), strTargetPath.c_str()).raw(),
1877 di.ulSuggestedSizeMB); // operation's weight, as set up with the IProgress originally
1878 }
1879 else
1880 {
1881 /* We need a proper source format description */
1882 ComObjPtr<MediumFormat> srcFormat;
1883 /* Which format to use? */
1884 Utf8Str strSrcFormat = "VDI";
1885 if ( di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#sparse", Utf8Str::CaseInsensitive)
1886 || di.strFormat.compare("http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized", Utf8Str::CaseInsensitive)
1887 || di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#compressed", Utf8Str::CaseInsensitive)
1888 || di.strFormat.compare("http://www.vmware.com/interfaces/specifications/vmdk.html#compressed", Utf8Str::CaseInsensitive)
1889 )
1890 strSrcFormat = "VMDK";
1891 srcFormat = pSysProps->mediumFormat(strSrcFormat);
1892 if (srcFormat.isNull())
1893 throw setError(VBOX_E_NOT_SUPPORTED,
1894 tr("Could not find a valid medium format for the source disk '%s'"),
1895 RTPathFilename(strSrcFilePath.c_str()));
1896
1897 /* Clone the source disk image */
1898 ComObjPtr<Medium> nullParent;
1899 rc = pTargetHD->importFile(strSrcFilePath.c_str(),
1900 srcFormat,
1901 MediumVariant_Standard,
1902 pCallbacks, pStorage,
1903 nullParent,
1904 pProgress);
1905 if (FAILED(rc)) throw rc;
1906
1907 /* Advance to the next operation. */
1908 stack.pProgress->SetNextOperation(BstrFmt(tr("Importing virtual disk image '%s'"), RTPathFilename(strSrcFilePath.c_str())).raw(),
1909 di.ulSuggestedSizeMB); // operation's weight, as set up with the IProgress originally);
1910 }
1911
1912 /* Now wait for the background disk operation to complete; this throws
1913 * HRESULTs on error. */
1914 ComPtr<IProgress> pp(pProgress);
1915 waitForAsyncProgress(stack.pProgress, pp);
1916
1917 /* Add the newly create disk path + a corresponding digest the our list for
1918 * later manifest verification. */
1919 stack.llSrcDisksDigest.push_back(STRPAIR(strSrcFilePath, pStorage ? pStorage->strDigest : ""));
1920}
1921
1922/**
1923 * Imports one OVF virtual system (described by the given ovf::VirtualSystem and VirtualSystemDescription)
1924 * into VirtualBox by creating an IMachine instance, which is returned.
1925 *
1926 * This throws HRESULT error codes for anything that goes wrong, in which case the caller must clean
1927 * up any leftovers from this function. For this, the given ImportStack instance has received information
1928 * about what needs cleaning up (to support rollback).
1929 *
1930 * @param vsysThis OVF virtual system (machine) to import.
1931 * @param vsdescThis Matching virtual system description (machine) to import.
1932 * @param pNewMachine out: Newly created machine.
1933 * @param stack Cleanup stack for when this throws.
1934 */
1935void Appliance::importMachineGeneric(const ovf::VirtualSystem &vsysThis,
1936 ComObjPtr<VirtualSystemDescription> &vsdescThis,
1937 ComPtr<IMachine> &pNewMachine,
1938 ImportStack &stack,
1939 PVDINTERFACEIO pCallbacks,
1940 PSHASTORAGE pStorage)
1941{
1942 HRESULT rc;
1943
1944 // Get the instance of IGuestOSType which matches our string guest OS type so we
1945 // can use recommended defaults for the new machine where OVF doesn't provide any
1946 ComPtr<IGuestOSType> osType;
1947 rc = mVirtualBox->GetGuestOSType(Bstr(stack.strOsTypeVBox).raw(), osType.asOutParam());
1948 if (FAILED(rc)) throw rc;
1949
1950 /* Create the machine */
1951 rc = mVirtualBox->CreateMachine(NULL, /* machine name: use default */
1952 Bstr(stack.strNameVBox).raw(),
1953 NULL, /* no groups */
1954 Bstr(stack.strOsTypeVBox).raw(),
1955 NULL, /* uuid */
1956 FALSE, /* fForceOverwrite */
1957 pNewMachine.asOutParam());
1958 if (FAILED(rc)) throw rc;
1959
1960 // set the description
1961 if (!stack.strDescription.isEmpty())
1962 {
1963 rc = pNewMachine->COMSETTER(Description)(Bstr(stack.strDescription).raw());
1964 if (FAILED(rc)) throw rc;
1965 }
1966
1967 // CPU count
1968 rc = pNewMachine->COMSETTER(CPUCount)(stack.cCPUs);
1969 if (FAILED(rc)) throw rc;
1970
1971 if (stack.fForceHWVirt)
1972 {
1973 rc = pNewMachine->SetHWVirtExProperty(HWVirtExPropertyType_Enabled, TRUE);
1974 if (FAILED(rc)) throw rc;
1975 }
1976
1977 // RAM
1978 rc = pNewMachine->COMSETTER(MemorySize)(stack.ulMemorySizeMB);
1979 if (FAILED(rc)) throw rc;
1980
1981 /* VRAM */
1982 /* Get the recommended VRAM for this guest OS type */
1983 ULONG vramVBox;
1984 rc = osType->COMGETTER(RecommendedVRAM)(&vramVBox);
1985 if (FAILED(rc)) throw rc;
1986
1987 /* Set the VRAM */
1988 rc = pNewMachine->COMSETTER(VRAMSize)(vramVBox);
1989 if (FAILED(rc)) throw rc;
1990
1991 // I/O APIC: Generic OVF has no setting for this. Enable it if we
1992 // import a Windows VM because if if Windows was installed without IOAPIC,
1993 // it will not mind finding an one later on, but if Windows was installed
1994 // _with_ an IOAPIC, it will bluescreen if it's not found
1995 if (!stack.fForceIOAPIC)
1996 {
1997 Bstr bstrFamilyId;
1998 rc = osType->COMGETTER(FamilyId)(bstrFamilyId.asOutParam());
1999 if (FAILED(rc)) throw rc;
2000 if (bstrFamilyId == "Windows")
2001 stack.fForceIOAPIC = true;
2002 }
2003
2004 if (stack.fForceIOAPIC)
2005 {
2006 ComPtr<IBIOSSettings> pBIOSSettings;
2007 rc = pNewMachine->COMGETTER(BIOSSettings)(pBIOSSettings.asOutParam());
2008 if (FAILED(rc)) throw rc;
2009
2010 rc = pBIOSSettings->COMSETTER(IOAPICEnabled)(TRUE);
2011 if (FAILED(rc)) throw rc;
2012 }
2013
2014 if (!stack.strAudioAdapter.isEmpty())
2015 if (stack.strAudioAdapter.compare("null", Utf8Str::CaseInsensitive) != 0)
2016 {
2017 uint32_t audio = RTStrToUInt32(stack.strAudioAdapter.c_str()); // should be 0 for AC97
2018 ComPtr<IAudioAdapter> audioAdapter;
2019 rc = pNewMachine->COMGETTER(AudioAdapter)(audioAdapter.asOutParam());
2020 if (FAILED(rc)) throw rc;
2021 rc = audioAdapter->COMSETTER(Enabled)(true);
2022 if (FAILED(rc)) throw rc;
2023 rc = audioAdapter->COMSETTER(AudioController)(static_cast<AudioControllerType_T>(audio));
2024 if (FAILED(rc)) throw rc;
2025 }
2026
2027#ifdef VBOX_WITH_USB
2028 /* USB Controller */
2029 ComPtr<IUSBController> usbController;
2030 rc = pNewMachine->COMGETTER(USBController)(usbController.asOutParam());
2031 if (FAILED(rc)) throw rc;
2032 rc = usbController->COMSETTER(Enabled)(stack.fUSBEnabled);
2033 if (FAILED(rc)) throw rc;
2034#endif /* VBOX_WITH_USB */
2035
2036 /* Change the network adapters */
2037 uint32_t maxNetworkAdapters = Global::getMaxNetworkAdapters(ChipsetType_PIIX3);
2038
2039 std::list<VirtualSystemDescriptionEntry*> vsdeNW = vsdescThis->findByType(VirtualSystemDescriptionType_NetworkAdapter);
2040 if (vsdeNW.size() == 0)
2041 {
2042 /* No network adapters, so we have to disable our default one */
2043 ComPtr<INetworkAdapter> nwVBox;
2044 rc = pNewMachine->GetNetworkAdapter(0, nwVBox.asOutParam());
2045 if (FAILED(rc)) throw rc;
2046 rc = nwVBox->COMSETTER(Enabled)(false);
2047 if (FAILED(rc)) throw rc;
2048 }
2049 else if (vsdeNW.size() > maxNetworkAdapters)
2050 throw setError(VBOX_E_FILE_ERROR,
2051 tr("Too many network adapters: OVF requests %d network adapters, but VirtualBox only supports %d"),
2052 vsdeNW.size(), maxNetworkAdapters);
2053 else
2054 {
2055 list<VirtualSystemDescriptionEntry*>::const_iterator nwIt;
2056 size_t a = 0;
2057 for (nwIt = vsdeNW.begin();
2058 nwIt != vsdeNW.end();
2059 ++nwIt, ++a)
2060 {
2061 const VirtualSystemDescriptionEntry* pvsys = *nwIt;
2062
2063 const Utf8Str &nwTypeVBox = pvsys->strVboxCurrent;
2064 uint32_t tt1 = RTStrToUInt32(nwTypeVBox.c_str());
2065 ComPtr<INetworkAdapter> pNetworkAdapter;
2066 rc = pNewMachine->GetNetworkAdapter((ULONG)a, pNetworkAdapter.asOutParam());
2067 if (FAILED(rc)) throw rc;
2068 /* Enable the network card & set the adapter type */
2069 rc = pNetworkAdapter->COMSETTER(Enabled)(true);
2070 if (FAILED(rc)) throw rc;
2071 rc = pNetworkAdapter->COMSETTER(AdapterType)(static_cast<NetworkAdapterType_T>(tt1));
2072 if (FAILED(rc)) throw rc;
2073
2074 // default is NAT; change to "bridged" if extra conf says so
2075 if (pvsys->strExtraConfigCurrent.endsWith("type=Bridged", Utf8Str::CaseInsensitive))
2076 {
2077 /* Attach to the right interface */
2078 rc = pNetworkAdapter->COMSETTER(AttachmentType)(NetworkAttachmentType_Bridged);
2079 if (FAILED(rc)) throw rc;
2080 ComPtr<IHost> host;
2081 rc = mVirtualBox->COMGETTER(Host)(host.asOutParam());
2082 if (FAILED(rc)) throw rc;
2083 com::SafeIfaceArray<IHostNetworkInterface> nwInterfaces;
2084 rc = host->COMGETTER(NetworkInterfaces)(ComSafeArrayAsOutParam(nwInterfaces));
2085 if (FAILED(rc)) throw rc;
2086 // We search for the first host network interface which
2087 // is usable for bridged networking
2088 for (size_t j = 0;
2089 j < nwInterfaces.size();
2090 ++j)
2091 {
2092 HostNetworkInterfaceType_T itype;
2093 rc = nwInterfaces[j]->COMGETTER(InterfaceType)(&itype);
2094 if (FAILED(rc)) throw rc;
2095 if (itype == HostNetworkInterfaceType_Bridged)
2096 {
2097 Bstr name;
2098 rc = nwInterfaces[j]->COMGETTER(Name)(name.asOutParam());
2099 if (FAILED(rc)) throw rc;
2100 /* Set the interface name to attach to */
2101 pNetworkAdapter->COMSETTER(BridgedInterface)(name.raw());
2102 if (FAILED(rc)) throw rc;
2103 break;
2104 }
2105 }
2106 }
2107 /* Next test for host only interfaces */
2108 else if (pvsys->strExtraConfigCurrent.endsWith("type=HostOnly", Utf8Str::CaseInsensitive))
2109 {
2110 /* Attach to the right interface */
2111 rc = pNetworkAdapter->COMSETTER(AttachmentType)(NetworkAttachmentType_HostOnly);
2112 if (FAILED(rc)) throw rc;
2113 ComPtr<IHost> host;
2114 rc = mVirtualBox->COMGETTER(Host)(host.asOutParam());
2115 if (FAILED(rc)) throw rc;
2116 com::SafeIfaceArray<IHostNetworkInterface> nwInterfaces;
2117 rc = host->COMGETTER(NetworkInterfaces)(ComSafeArrayAsOutParam(nwInterfaces));
2118 if (FAILED(rc)) throw rc;
2119 // We search for the first host network interface which
2120 // is usable for host only networking
2121 for (size_t j = 0;
2122 j < nwInterfaces.size();
2123 ++j)
2124 {
2125 HostNetworkInterfaceType_T itype;
2126 rc = nwInterfaces[j]->COMGETTER(InterfaceType)(&itype);
2127 if (FAILED(rc)) throw rc;
2128 if (itype == HostNetworkInterfaceType_HostOnly)
2129 {
2130 Bstr name;
2131 rc = nwInterfaces[j]->COMGETTER(Name)(name.asOutParam());
2132 if (FAILED(rc)) throw rc;
2133 /* Set the interface name to attach to */
2134 pNetworkAdapter->COMSETTER(HostOnlyInterface)(name.raw());
2135 if (FAILED(rc)) throw rc;
2136 break;
2137 }
2138 }
2139 }
2140 /* Next test for internal interfaces */
2141 else if (pvsys->strExtraConfigCurrent.endsWith("type=Internal", Utf8Str::CaseInsensitive))
2142 {
2143 /* Attach to the right interface */
2144 rc = pNetworkAdapter->COMSETTER(AttachmentType)(NetworkAttachmentType_Internal);
2145 if (FAILED(rc)) throw rc;
2146 }
2147 /* Next test for Generic interfaces */
2148 else if (pvsys->strExtraConfigCurrent.endsWith("type=Generic", Utf8Str::CaseInsensitive))
2149 {
2150 /* Attach to the right interface */
2151 rc = pNetworkAdapter->COMSETTER(AttachmentType)(NetworkAttachmentType_Generic);
2152 if (FAILED(rc)) throw rc;
2153 }
2154 }
2155 }
2156
2157 // IDE Hard disk controller
2158 std::list<VirtualSystemDescriptionEntry*> vsdeHDCIDE = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerIDE);
2159 // In OVF (at least VMware's version of it), an IDE controller has two ports, so VirtualBox's single IDE controller
2160 // with two channels and two ports each counts as two OVF IDE controllers -- so we accept one or two such IDE controllers
2161 size_t cIDEControllers = vsdeHDCIDE.size();
2162 if (cIDEControllers > 2)
2163 throw setError(VBOX_E_FILE_ERROR,
2164 tr("Too many IDE controllers in OVF; import facility only supports two"));
2165 if (vsdeHDCIDE.size() > 0)
2166 {
2167 // one or two IDE controllers present in OVF: add one VirtualBox controller
2168 ComPtr<IStorageController> pController;
2169 rc = pNewMachine->AddStorageController(Bstr("IDE Controller").raw(), StorageBus_IDE, pController.asOutParam());
2170 if (FAILED(rc)) throw rc;
2171
2172 const char *pcszIDEType = vsdeHDCIDE.front()->strVboxCurrent.c_str();
2173 if (!strcmp(pcszIDEType, "PIIX3"))
2174 rc = pController->COMSETTER(ControllerType)(StorageControllerType_PIIX3);
2175 else if (!strcmp(pcszIDEType, "PIIX4"))
2176 rc = pController->COMSETTER(ControllerType)(StorageControllerType_PIIX4);
2177 else if (!strcmp(pcszIDEType, "ICH6"))
2178 rc = pController->COMSETTER(ControllerType)(StorageControllerType_ICH6);
2179 else
2180 throw setError(VBOX_E_FILE_ERROR,
2181 tr("Invalid IDE controller type \"%s\""),
2182 pcszIDEType);
2183 if (FAILED(rc)) throw rc;
2184 }
2185
2186 /* Hard disk controller SATA */
2187 std::list<VirtualSystemDescriptionEntry*> vsdeHDCSATA = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerSATA);
2188 if (vsdeHDCSATA.size() > 1)
2189 throw setError(VBOX_E_FILE_ERROR,
2190 tr("Too many SATA controllers in OVF; import facility only supports one"));
2191 if (vsdeHDCSATA.size() > 0)
2192 {
2193 ComPtr<IStorageController> pController;
2194 const Utf8Str &hdcVBox = vsdeHDCSATA.front()->strVboxCurrent;
2195 if (hdcVBox == "AHCI")
2196 {
2197 rc = pNewMachine->AddStorageController(Bstr("SATA Controller").raw(), StorageBus_SATA, pController.asOutParam());
2198 if (FAILED(rc)) throw rc;
2199 }
2200 else
2201 throw setError(VBOX_E_FILE_ERROR,
2202 tr("Invalid SATA controller type \"%s\""),
2203 hdcVBox.c_str());
2204 }
2205
2206 /* Hard disk controller SCSI */
2207 std::list<VirtualSystemDescriptionEntry*> vsdeHDCSCSI = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerSCSI);
2208 if (vsdeHDCSCSI.size() > 1)
2209 throw setError(VBOX_E_FILE_ERROR,
2210 tr("Too many SCSI controllers in OVF; import facility only supports one"));
2211 if (vsdeHDCSCSI.size() > 0)
2212 {
2213 ComPtr<IStorageController> pController;
2214 Bstr bstrName(L"SCSI Controller");
2215 StorageBus_T busType = StorageBus_SCSI;
2216 StorageControllerType_T controllerType;
2217 const Utf8Str &hdcVBox = vsdeHDCSCSI.front()->strVboxCurrent;
2218 if (hdcVBox == "LsiLogic")
2219 controllerType = StorageControllerType_LsiLogic;
2220 else if (hdcVBox == "LsiLogicSas")
2221 {
2222 // OVF treats LsiLogicSas as a SCSI controller but VBox considers it a class of its own
2223 bstrName = L"SAS Controller";
2224 busType = StorageBus_SAS;
2225 controllerType = StorageControllerType_LsiLogicSas;
2226 }
2227 else if (hdcVBox == "BusLogic")
2228 controllerType = StorageControllerType_BusLogic;
2229 else
2230 throw setError(VBOX_E_FILE_ERROR,
2231 tr("Invalid SCSI controller type \"%s\""),
2232 hdcVBox.c_str());
2233
2234 rc = pNewMachine->AddStorageController(bstrName.raw(), busType, pController.asOutParam());
2235 if (FAILED(rc)) throw rc;
2236 rc = pController->COMSETTER(ControllerType)(controllerType);
2237 if (FAILED(rc)) throw rc;
2238 }
2239
2240 /* Hard disk controller SAS */
2241 std::list<VirtualSystemDescriptionEntry*> vsdeHDCSAS = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerSAS);
2242 if (vsdeHDCSAS.size() > 1)
2243 throw setError(VBOX_E_FILE_ERROR,
2244 tr("Too many SAS controllers in OVF; import facility only supports one"));
2245 if (vsdeHDCSAS.size() > 0)
2246 {
2247 ComPtr<IStorageController> pController;
2248 rc = pNewMachine->AddStorageController(Bstr(L"SAS Controller").raw(), StorageBus_SAS, pController.asOutParam());
2249 if (FAILED(rc)) throw rc;
2250 rc = pController->COMSETTER(ControllerType)(StorageControllerType_LsiLogicSas);
2251 if (FAILED(rc)) throw rc;
2252 }
2253
2254 /* Now its time to register the machine before we add any hard disks */
2255 rc = mVirtualBox->RegisterMachine(pNewMachine);
2256 if (FAILED(rc)) throw rc;
2257
2258 // store new machine for roll-back in case of errors
2259 Bstr bstrNewMachineId;
2260 rc = pNewMachine->COMGETTER(Id)(bstrNewMachineId.asOutParam());
2261 if (FAILED(rc)) throw rc;
2262 Guid uuidNewMachine(bstrNewMachineId);
2263 m->llGuidsMachinesCreated.push_back(uuidNewMachine);
2264
2265 // Add floppies and CD-ROMs to the appropriate controllers.
2266 std::list<VirtualSystemDescriptionEntry*> vsdeFloppy = vsdescThis->findByType(VirtualSystemDescriptionType_Floppy);
2267 if (vsdeFloppy.size() > 1)
2268 throw setError(VBOX_E_FILE_ERROR,
2269 tr("Too many floppy controllers in OVF; import facility only supports one"));
2270 std::list<VirtualSystemDescriptionEntry*> vsdeCDROM = vsdescThis->findByType(VirtualSystemDescriptionType_CDROM);
2271 if ( (vsdeFloppy.size() > 0)
2272 || (vsdeCDROM.size() > 0)
2273 )
2274 {
2275 // If there's an error here we need to close the session, so
2276 // we need another try/catch block.
2277
2278 try
2279 {
2280 // to attach things we need to open a session for the new machine
2281 rc = pNewMachine->LockMachine(stack.pSession, LockType_Write);
2282 if (FAILED(rc)) throw rc;
2283 stack.fSessionOpen = true;
2284
2285 ComPtr<IMachine> sMachine;
2286 rc = stack.pSession->COMGETTER(Machine)(sMachine.asOutParam());
2287 if (FAILED(rc)) throw rc;
2288
2289 // floppy first
2290 if (vsdeFloppy.size() == 1)
2291 {
2292 ComPtr<IStorageController> pController;
2293 rc = sMachine->AddStorageController(Bstr("Floppy Controller").raw(), StorageBus_Floppy, pController.asOutParam());
2294 if (FAILED(rc)) throw rc;
2295
2296 Bstr bstrName;
2297 rc = pController->COMGETTER(Name)(bstrName.asOutParam());
2298 if (FAILED(rc)) throw rc;
2299
2300 // this is for rollback later
2301 MyHardDiskAttachment mhda;
2302 mhda.pMachine = pNewMachine;
2303 mhda.controllerType = bstrName;
2304 mhda.lControllerPort = 0;
2305 mhda.lDevice = 0;
2306
2307 Log(("Attaching floppy\n"));
2308
2309 rc = sMachine->AttachDevice(mhda.controllerType.raw(),
2310 mhda.lControllerPort,
2311 mhda.lDevice,
2312 DeviceType_Floppy,
2313 NULL);
2314 if (FAILED(rc)) throw rc;
2315
2316 stack.llHardDiskAttachments.push_back(mhda);
2317 }
2318
2319 // CD-ROMs next
2320 for (std::list<VirtualSystemDescriptionEntry*>::const_iterator jt = vsdeCDROM.begin();
2321 jt != vsdeCDROM.end();
2322 ++jt)
2323 {
2324 // for now always attach to secondary master on IDE controller;
2325 // there seems to be no useful information in OVF where else to
2326 // attach it (@todo test with latest versions of OVF software)
2327
2328 // find the IDE controller
2329 const ovf::HardDiskController *pController = NULL;
2330 for (ovf::ControllersMap::const_iterator kt = vsysThis.mapControllers.begin();
2331 kt != vsysThis.mapControllers.end();
2332 ++kt)
2333 {
2334 if (kt->second.system == ovf::HardDiskController::IDE)
2335 {
2336 pController = &kt->second;
2337 break;
2338 }
2339 }
2340
2341 if (!pController)
2342 throw setError(VBOX_E_FILE_ERROR,
2343 tr("OVF wants a CD-ROM drive but cannot find IDE controller, which is required in this version of VirtualBox"));
2344
2345 // this is for rollback later
2346 MyHardDiskAttachment mhda;
2347 mhda.pMachine = pNewMachine;
2348
2349 convertDiskAttachmentValues(*pController,
2350 2, // interpreted as secondary master
2351 mhda.controllerType, // Bstr
2352 mhda.lControllerPort,
2353 mhda.lDevice);
2354
2355 Log(("Attaching CD-ROM to port %d on device %d\n", mhda.lControllerPort, mhda.lDevice));
2356
2357 rc = sMachine->AttachDevice(mhda.controllerType.raw(),
2358 mhda.lControllerPort,
2359 mhda.lDevice,
2360 DeviceType_DVD,
2361 NULL);
2362 if (FAILED(rc)) throw rc;
2363
2364 stack.llHardDiskAttachments.push_back(mhda);
2365 } // end for (itHD = avsdeHDs.begin();
2366
2367 rc = sMachine->SaveSettings();
2368 if (FAILED(rc)) throw rc;
2369
2370 // only now that we're done with all disks, close the session
2371 rc = stack.pSession->UnlockMachine();
2372 if (FAILED(rc)) throw rc;
2373 stack.fSessionOpen = false;
2374 }
2375 catch(HRESULT /* aRC */)
2376 {
2377 if (stack.fSessionOpen)
2378 stack.pSession->UnlockMachine();
2379
2380 throw;
2381 }
2382 }
2383
2384 // create the hard disks & connect them to the appropriate controllers
2385 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);
2386 if (avsdeHDs.size() > 0)
2387 {
2388 // If there's an error here we need to close the session, so
2389 // we need another try/catch block.
2390 try
2391 {
2392 // to attach things we need to open a session for the new machine
2393 rc = pNewMachine->LockMachine(stack.pSession, LockType_Write);
2394 if (FAILED(rc)) throw rc;
2395 stack.fSessionOpen = true;
2396
2397 /* Iterate over all given disk images */
2398 list<VirtualSystemDescriptionEntry*>::const_iterator itHD;
2399 for (itHD = avsdeHDs.begin();
2400 itHD != avsdeHDs.end();
2401 ++itHD)
2402 {
2403 VirtualSystemDescriptionEntry *vsdeHD = *itHD;
2404
2405 // vsdeHD->strRef contains the disk identifier (e.g. "vmdisk1"), which should exist
2406 // in the virtual system's disks map under that ID and also in the global images map
2407 ovf::VirtualDisksMap::const_iterator itVirtualDisk = vsysThis.mapVirtualDisks.find(vsdeHD->strRef);
2408 // and find the disk from the OVF's disk list
2409 ovf::DiskImagesMap::const_iterator itDiskImage = stack.mapDisks.find(vsdeHD->strRef);
2410 if ( (itVirtualDisk == vsysThis.mapVirtualDisks.end())
2411 || (itDiskImage == stack.mapDisks.end())
2412 )
2413 throw setError(E_FAIL,
2414 tr("Internal inconsistency looking up disk image '%s'"),
2415 vsdeHD->strRef.c_str());
2416
2417 const ovf::DiskImage &ovfDiskImage = itDiskImage->second;
2418 const ovf::VirtualDisk &ovfVdisk = itVirtualDisk->second;
2419
2420 ComObjPtr<Medium> pTargetHD;
2421 importOneDiskImage(ovfDiskImage,
2422 vsdeHD->strVboxCurrent,
2423 pTargetHD,
2424 stack,
2425 pCallbacks,
2426 pStorage);
2427
2428 // now use the new uuid to attach the disk image to our new machine
2429 ComPtr<IMachine> sMachine;
2430 rc = stack.pSession->COMGETTER(Machine)(sMachine.asOutParam());
2431 if (FAILED(rc)) throw rc;
2432
2433 // find the hard disk controller to which we should attach
2434 ovf::HardDiskController hdc = (*vsysThis.mapControllers.find(ovfVdisk.idController)).second;
2435
2436 // this is for rollback later
2437 MyHardDiskAttachment mhda;
2438 mhda.pMachine = pNewMachine;
2439
2440 convertDiskAttachmentValues(hdc,
2441 ovfVdisk.ulAddressOnParent,
2442 mhda.controllerType, // Bstr
2443 mhda.lControllerPort,
2444 mhda.lDevice);
2445
2446 Log(("Attaching disk %s to port %d on device %d\n", vsdeHD->strVboxCurrent.c_str(), mhda.lControllerPort, mhda.lDevice));
2447
2448 rc = sMachine->AttachDevice(mhda.controllerType.raw(), // wstring name
2449 mhda.lControllerPort, // long controllerPort
2450 mhda.lDevice, // long device
2451 DeviceType_HardDisk, // DeviceType_T type
2452 pTargetHD);
2453 if (FAILED(rc)) throw rc;
2454
2455 stack.llHardDiskAttachments.push_back(mhda);
2456
2457 rc = sMachine->SaveSettings();
2458 if (FAILED(rc)) throw rc;
2459 } // end for (itHD = avsdeHDs.begin();
2460
2461 // only now that we're done with all disks, close the session
2462 rc = stack.pSession->UnlockMachine();
2463 if (FAILED(rc)) throw rc;
2464 stack.fSessionOpen = false;
2465 }
2466 catch(HRESULT /* aRC */)
2467 {
2468 if (stack.fSessionOpen)
2469 stack.pSession->UnlockMachine();
2470
2471 throw;
2472 }
2473 }
2474}
2475
2476/**
2477 * Imports one OVF virtual system (described by a vbox:Machine tag represented by the given config
2478 * structure) into VirtualBox by creating an IMachine instance, which is returned.
2479 *
2480 * This throws HRESULT error codes for anything that goes wrong, in which case the caller must clean
2481 * up any leftovers from this function. For this, the given ImportStack instance has received information
2482 * about what needs cleaning up (to support rollback).
2483 *
2484 * The machine config stored in the settings::MachineConfigFile structure contains the UUIDs of
2485 * the disk attachments used by the machine when it was exported. We also add vbox:uuid attributes
2486 * to the OVF disks sections so we can look them up. While importing these UUIDs into a second host
2487 * will most probably work, reimporting them into the same host will cause conflicts, so we always
2488 * generate new ones on import. This involves the following:
2489 *
2490 * 1) Scan the machine config for disk attachments.
2491 *
2492 * 2) For each disk attachment found, look up the OVF disk image from the disk references section
2493 * and import the disk into VirtualBox, which creates a new UUID for it. In the machine config,
2494 * replace the old UUID with the new one.
2495 *
2496 * 3) Change the machine config according to the OVF virtual system descriptions, in case the
2497 * caller has modified them using setFinalValues().
2498 *
2499 * 4) Create the VirtualBox machine with the modfified machine config.
2500 *
2501 * @param config
2502 * @param pNewMachine
2503 * @param stack
2504 */
2505void Appliance::importVBoxMachine(ComObjPtr<VirtualSystemDescription> &vsdescThis,
2506 ComPtr<IMachine> &pReturnNewMachine,
2507 ImportStack &stack,
2508 PVDINTERFACEIO pCallbacks,
2509 PSHASTORAGE pStorage)
2510{
2511 Assert(vsdescThis->m->pConfig);
2512
2513 HRESULT rc = S_OK;
2514
2515 settings::MachineConfigFile &config = *vsdescThis->m->pConfig;
2516
2517 /*
2518 *
2519 * step 1): modify machine config according to OVF config, in case the user
2520 * has modified them using setFinalValues()
2521 *
2522 */
2523
2524 /* OS Type */
2525 config.machineUserData.strOsType = stack.strOsTypeVBox;
2526 /* Description */
2527 config.machineUserData.strDescription = stack.strDescription;
2528 /* CPU count & extented attributes */
2529 config.hardwareMachine.cCPUs = stack.cCPUs;
2530 if (stack.fForceIOAPIC)
2531 config.hardwareMachine.fHardwareVirt = true;
2532 if (stack.fForceIOAPIC)
2533 config.hardwareMachine.biosSettings.fIOAPICEnabled = true;
2534 /* RAM size */
2535 config.hardwareMachine.ulMemorySizeMB = stack.ulMemorySizeMB;
2536
2537/*
2538 <const name="HardDiskControllerIDE" value="14" />
2539 <const name="HardDiskControllerSATA" value="15" />
2540 <const name="HardDiskControllerSCSI" value="16" />
2541 <const name="HardDiskControllerSAS" value="17" />
2542*/
2543
2544#ifdef VBOX_WITH_USB
2545 /* USB controller */
2546 config.hardwareMachine.usbController.fEnabled = stack.fUSBEnabled;
2547#endif
2548 /* Audio adapter */
2549 if (stack.strAudioAdapter.isNotEmpty())
2550 {
2551 config.hardwareMachine.audioAdapter.fEnabled = true;
2552 config.hardwareMachine.audioAdapter.controllerType = (AudioControllerType_T)stack.strAudioAdapter.toUInt32();
2553 }
2554 else
2555 config.hardwareMachine.audioAdapter.fEnabled = false;
2556 /* Network adapter */
2557 settings::NetworkAdaptersList &llNetworkAdapters = config.hardwareMachine.llNetworkAdapters;
2558 /* First disable all network cards, they will be enabled below again. */
2559 settings::NetworkAdaptersList::iterator it1;
2560 bool fKeepAllMACs = m->optList.contains(ImportOptions_KeepAllMACs);
2561 bool fKeepNATMACs = m->optList.contains(ImportOptions_KeepNATMACs);
2562 for (it1 = llNetworkAdapters.begin(); it1 != llNetworkAdapters.end(); ++it1)
2563 {
2564 it1->fEnabled = false;
2565 if (!( fKeepAllMACs
2566 || (fKeepNATMACs && it1->mode == NetworkAttachmentType_NAT)))
2567 Host::generateMACAddress(it1->strMACAddress);
2568 }
2569 /* Now iterate over all network entries. */
2570 std::list<VirtualSystemDescriptionEntry*> avsdeNWs = vsdescThis->findByType(VirtualSystemDescriptionType_NetworkAdapter);
2571 if (avsdeNWs.size() > 0)
2572 {
2573 /* Iterate through all network adapter entries and search for the
2574 * corresponding one in the machine config. If one is found, configure
2575 * it based on the user settings. */
2576 list<VirtualSystemDescriptionEntry*>::const_iterator itNW;
2577 for (itNW = avsdeNWs.begin();
2578 itNW != avsdeNWs.end();
2579 ++itNW)
2580 {
2581 VirtualSystemDescriptionEntry *vsdeNW = *itNW;
2582 if ( vsdeNW->strExtraConfigCurrent.startsWith("slot=", Utf8Str::CaseInsensitive)
2583 && vsdeNW->strExtraConfigCurrent.length() > 6)
2584 {
2585 uint32_t iSlot = vsdeNW->strExtraConfigCurrent.substr(5, 1).toUInt32();
2586 /* Iterate through all network adapters in the machine config. */
2587 for (it1 = llNetworkAdapters.begin();
2588 it1 != llNetworkAdapters.end();
2589 ++it1)
2590 {
2591 /* Compare the slots. */
2592 if (it1->ulSlot == iSlot)
2593 {
2594 it1->fEnabled = true;
2595 it1->type = (NetworkAdapterType_T)vsdeNW->strVboxCurrent.toUInt32();
2596 break;
2597 }
2598 }
2599 }
2600 }
2601 }
2602
2603 /* Floppy controller */
2604 bool fFloppy = vsdescThis->findByType(VirtualSystemDescriptionType_Floppy).size() > 0;
2605 /* DVD controller */
2606 bool fDVD = vsdescThis->findByType(VirtualSystemDescriptionType_CDROM).size() > 0;
2607 /* Iterate over all storage controller check the attachments and remove
2608 * them when necessary. Also detect broken configs with more than one
2609 * attachment. Old VirtualBox versions (prior to 3.2.10) had all disk
2610 * attachments pointing to the last hard disk image, which causes import
2611 * failures. A long fixed bug, however the OVF files are long lived. */
2612 settings::StorageControllersList &llControllers = config.storageMachine.llStorageControllers;
2613 Guid hdUuid;
2614 uint32_t cHardDisks = 0;
2615 bool fInconsistent = false;
2616 bool fRepairDuplicate = false;
2617 settings::StorageControllersList::iterator it3;
2618 for (it3 = llControllers.begin();
2619 it3 != llControllers.end();
2620 ++it3)
2621 {
2622 settings::AttachedDevicesList &llAttachments = it3->llAttachedDevices;
2623 settings::AttachedDevicesList::iterator it4 = llAttachments.begin();
2624 while (it4 != llAttachments.end())
2625 {
2626 if ( ( !fDVD
2627 && it4->deviceType == DeviceType_DVD)
2628 ||
2629 ( !fFloppy
2630 && it4->deviceType == DeviceType_Floppy))
2631 {
2632 it4 = llAttachments.erase(it4);
2633 continue;
2634 }
2635 else if (it4->deviceType == DeviceType_HardDisk)
2636 {
2637 const Guid &thisUuid = it4->uuid;
2638 cHardDisks++;
2639 if (cHardDisks == 1)
2640 {
2641 if (hdUuid.isEmpty())
2642 hdUuid = thisUuid;
2643 else
2644 fInconsistent = true;
2645 }
2646 else
2647 {
2648 if (thisUuid.isEmpty())
2649 fInconsistent = true;
2650 else if (thisUuid == hdUuid)
2651 fRepairDuplicate = true;
2652 }
2653 }
2654 ++it4;
2655 }
2656 }
2657 /* paranoia... */
2658 if (fInconsistent || cHardDisks == 1)
2659 fRepairDuplicate = false;
2660
2661 /*
2662 *
2663 * step 2: scan the machine config for media attachments
2664 *
2665 */
2666
2667 /* Get all hard disk descriptions. */
2668 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);
2669 std::list<VirtualSystemDescriptionEntry*>::iterator avsdeHDsIt = avsdeHDs.begin();
2670 /* paranoia - if there is no 1:1 match do not try to repair. */
2671 if (cHardDisks != avsdeHDs.size())
2672 fRepairDuplicate = false;
2673
2674 // for each storage controller...
2675 for (settings::StorageControllersList::iterator sit = config.storageMachine.llStorageControllers.begin();
2676 sit != config.storageMachine.llStorageControllers.end();
2677 ++sit)
2678 {
2679 settings::StorageController &sc = *sit;
2680
2681 // find the OVF virtual system description entry for this storage controller
2682 switch (sc.storageBus)
2683 {
2684 case StorageBus_SATA:
2685 break;
2686 case StorageBus_SCSI:
2687 break;
2688 case StorageBus_IDE:
2689 break;
2690 case StorageBus_SAS:
2691 break;
2692 }
2693
2694 // for each medium attachment to this controller...
2695 for (settings::AttachedDevicesList::iterator dit = sc.llAttachedDevices.begin();
2696 dit != sc.llAttachedDevices.end();
2697 ++dit)
2698 {
2699 settings::AttachedDevice &d = *dit;
2700
2701 if (d.uuid.isEmpty())
2702 // empty DVD and floppy media
2703 continue;
2704
2705 // When repairing a broken VirtualBox xml config section (written
2706 // by VirtualBox versions earlier than 3.2.10) assume the disks
2707 // show up in the same order as in the OVF description.
2708 if (fRepairDuplicate)
2709 {
2710 VirtualSystemDescriptionEntry *vsdeHD = *avsdeHDsIt;
2711 ovf::DiskImagesMap::const_iterator itDiskImage = stack.mapDisks.find(vsdeHD->strRef);
2712 if (itDiskImage != stack.mapDisks.end())
2713 {
2714 const ovf::DiskImage &di = itDiskImage->second;
2715 d.uuid = Guid(di.uuidVbox);
2716 }
2717 ++avsdeHDsIt;
2718 }
2719
2720 // convert the Guid to string
2721 Utf8Str strUuid = d.uuid.toString();
2722
2723 // there must be an image in the OVF disk structs with the same UUID
2724 bool fFound = false;
2725 for (ovf::DiskImagesMap::const_iterator oit = stack.mapDisks.begin();
2726 oit != stack.mapDisks.end();
2727 ++oit)
2728 {
2729 const ovf::DiskImage &di = oit->second;
2730
2731 if (di.uuidVbox == strUuid)
2732 {
2733 VirtualSystemDescriptionEntry *vsdeTargetHD = 0;
2734
2735 /* Iterate over all given disk images of the virtual system
2736 * disks description. We need to find the target disk path,
2737 * which could be changed by the user. */
2738 list<VirtualSystemDescriptionEntry*>::const_iterator itHD;
2739 for (itHD = avsdeHDs.begin();
2740 itHD != avsdeHDs.end();
2741 ++itHD)
2742 {
2743 VirtualSystemDescriptionEntry *vsdeHD = *itHD;
2744 if (vsdeHD->strRef == oit->first)
2745 {
2746 vsdeTargetHD = vsdeHD;
2747 break;
2748 }
2749 }
2750 if (!vsdeTargetHD)
2751 throw setError(E_FAIL,
2752 tr("Internal inconsistency looking up disk image '%s'"),
2753 oit->first.c_str());
2754
2755 /*
2756 *
2757 * step 3: import disk
2758 *
2759 */
2760 ComObjPtr<Medium> pTargetHD;
2761 importOneDiskImage(di,
2762 vsdeTargetHD->strVboxCurrent,
2763 pTargetHD,
2764 stack,
2765 pCallbacks,
2766 pStorage);
2767
2768 // ... and replace the old UUID in the machine config with the one of
2769 // the imported disk that was just created
2770 Bstr hdId;
2771 rc = pTargetHD->COMGETTER(Id)(hdId.asOutParam());
2772 if (FAILED(rc)) throw rc;
2773
2774 d.uuid = hdId;
2775
2776 fFound = true;
2777 break;
2778 }
2779 }
2780
2781 // no disk with such a UUID found:
2782 if (!fFound)
2783 throw setError(E_FAIL,
2784 tr("<vbox:Machine> element in OVF contains a medium attachment for the disk image %s but the OVF describes no such image"),
2785 strUuid.c_str());
2786 } // for (settings::AttachedDevicesList::const_iterator dit = sc.llAttachedDevices.begin();
2787 } // for (settings::StorageControllersList::const_iterator sit = config.storageMachine.llStorageControllers.begin();
2788
2789 /*
2790 *
2791 * step 4): create the machine and have it import the config
2792 *
2793 */
2794
2795 ComObjPtr<Machine> pNewMachine;
2796 rc = pNewMachine.createObject();
2797 if (FAILED(rc)) throw rc;
2798
2799 // this magic constructor fills the new machine object with the MachineConfig
2800 // instance that we created from the vbox:Machine
2801 rc = pNewMachine->init(mVirtualBox,
2802 stack.strNameVBox, // name from OVF preparations; can be suffixed to avoid duplicates, or changed by user
2803 config); // the whole machine config
2804 if (FAILED(rc)) throw rc;
2805
2806 pReturnNewMachine = ComPtr<IMachine>(pNewMachine);
2807
2808 // and register it
2809 rc = mVirtualBox->RegisterMachine(pNewMachine);
2810 if (FAILED(rc)) throw rc;
2811
2812 // store new machine for roll-back in case of errors
2813 Bstr bstrNewMachineId;
2814 rc = pNewMachine->COMGETTER(Id)(bstrNewMachineId.asOutParam());
2815 if (FAILED(rc)) throw rc;
2816 m->llGuidsMachinesCreated.push_back(Guid(bstrNewMachineId));
2817}
2818
2819void Appliance::importMachines(ImportStack &stack,
2820 PVDINTERFACEIO pCallbacks,
2821 PSHASTORAGE pStorage)
2822{
2823 HRESULT rc = S_OK;
2824
2825 // this is safe to access because this thread only gets started
2826 // if pReader != NULL
2827 const ovf::OVFReader &reader = *m->pReader;
2828
2829 // create a session for the machine + disks we manipulate below
2830 rc = stack.pSession.createInprocObject(CLSID_Session);
2831 if (FAILED(rc)) throw rc;
2832
2833 list<ovf::VirtualSystem>::const_iterator it;
2834 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it1;
2835 /* Iterate through all virtual systems of that appliance */
2836 size_t i = 0;
2837 for (it = reader.m_llVirtualSystems.begin(),
2838 it1 = m->virtualSystemDescriptions.begin();
2839 it != reader.m_llVirtualSystems.end();
2840 ++it, ++it1, ++i)
2841 {
2842 const ovf::VirtualSystem &vsysThis = *it;
2843 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it1);
2844
2845 ComPtr<IMachine> pNewMachine;
2846
2847 // there are two ways in which we can create a vbox machine from OVF:
2848 // -- either this OVF was written by vbox 3.2 or later, in which case there is a <vbox:Machine> element
2849 // in the <VirtualSystem>; then the VirtualSystemDescription::Data has a settings::MachineConfigFile
2850 // with all the machine config pretty-parsed;
2851 // -- or this is an OVF from an older vbox or an external source, and then we need to translate the
2852 // VirtualSystemDescriptionEntry and do import work
2853
2854 // Even for the vbox:Machine case, there are a number of configuration items that will be taken from
2855 // the OVF because otherwise the "override import parameters" mechanism in the GUI won't work.
2856
2857 // VM name
2858 std::list<VirtualSystemDescriptionEntry*> vsdeName = vsdescThis->findByType(VirtualSystemDescriptionType_Name);
2859 if (vsdeName.size() < 1)
2860 throw setError(VBOX_E_FILE_ERROR,
2861 tr("Missing VM name"));
2862 stack.strNameVBox = vsdeName.front()->strVboxCurrent;
2863
2864 // have VirtualBox suggest where the filename would be placed so we can
2865 // put the disk images in the same directory
2866 Bstr bstrMachineFilename;
2867 rc = mVirtualBox->ComposeMachineFilename(Bstr(stack.strNameVBox).raw(),
2868 NULL /* aGroup */,
2869 NULL /* aBaseFolder */,
2870 bstrMachineFilename.asOutParam());
2871 if (FAILED(rc)) throw rc;
2872 // and determine the machine folder from that
2873 stack.strMachineFolder = bstrMachineFilename;
2874 stack.strMachineFolder.stripFilename();
2875
2876 // guest OS type
2877 std::list<VirtualSystemDescriptionEntry*> vsdeOS;
2878 vsdeOS = vsdescThis->findByType(VirtualSystemDescriptionType_OS);
2879 if (vsdeOS.size() < 1)
2880 throw setError(VBOX_E_FILE_ERROR,
2881 tr("Missing guest OS type"));
2882 stack.strOsTypeVBox = vsdeOS.front()->strVboxCurrent;
2883
2884 // CPU count
2885 std::list<VirtualSystemDescriptionEntry*> vsdeCPU = vsdescThis->findByType(VirtualSystemDescriptionType_CPU);
2886 if (vsdeCPU.size() != 1)
2887 throw setError(VBOX_E_FILE_ERROR, tr("CPU count missing"));
2888
2889 stack.cCPUs = vsdeCPU.front()->strVboxCurrent.toUInt32();
2890 // We need HWVirt & IO-APIC if more than one CPU is requested
2891 if (stack.cCPUs > 1)
2892 {
2893 stack.fForceHWVirt = true;
2894 stack.fForceIOAPIC = true;
2895 }
2896
2897 // RAM
2898 std::list<VirtualSystemDescriptionEntry*> vsdeRAM = vsdescThis->findByType(VirtualSystemDescriptionType_Memory);
2899 if (vsdeRAM.size() != 1)
2900 throw setError(VBOX_E_FILE_ERROR, tr("RAM size missing"));
2901 stack.ulMemorySizeMB = (ULONG)vsdeRAM.front()->strVboxCurrent.toUInt64();
2902
2903#ifdef VBOX_WITH_USB
2904 // USB controller
2905 std::list<VirtualSystemDescriptionEntry*> vsdeUSBController = vsdescThis->findByType(VirtualSystemDescriptionType_USBController);
2906 // USB support is enabled if there's at least one such entry; to disable USB support,
2907 // the type of the USB item would have been changed to "ignore"
2908 stack.fUSBEnabled = vsdeUSBController.size() > 0;
2909#endif
2910 // audio adapter
2911 std::list<VirtualSystemDescriptionEntry*> vsdeAudioAdapter = vsdescThis->findByType(VirtualSystemDescriptionType_SoundCard);
2912 /* @todo: we support one audio adapter only */
2913 if (vsdeAudioAdapter.size() > 0)
2914 stack.strAudioAdapter = vsdeAudioAdapter.front()->strVboxCurrent;
2915
2916 // for the description of the new machine, always use the OVF entry, the user may have changed it in the import config
2917 std::list<VirtualSystemDescriptionEntry*> vsdeDescription = vsdescThis->findByType(VirtualSystemDescriptionType_Description);
2918 if (vsdeDescription.size())
2919 stack.strDescription = vsdeDescription.front()->strVboxCurrent;
2920
2921 // import vbox:machine or OVF now
2922 if (vsdescThis->m->pConfig)
2923 // vbox:Machine config
2924 importVBoxMachine(vsdescThis, pNewMachine, stack, pCallbacks, pStorage);
2925 else
2926 // generic OVF config
2927 importMachineGeneric(vsysThis, vsdescThis, pNewMachine, stack, pCallbacks, pStorage);
2928
2929 } // for (it = pAppliance->m->llVirtualSystems.begin() ...
2930}
2931
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