VirtualBox

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

Last change on this file since 38469 was 38469, checked in by vboxsync, 14 years ago

VD: Interface cleanup. Merge the two involved structures (generic interface descriptor and callback table) into one, remove the duplicated interface wrappers in the backends and move the interface definitions into separate headers separating public and private interfaces.

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