VirtualBox

source: vbox/trunk/src/VBox/Main/ApplianceImpl.cpp@ 18265

最後變更 在這個檔案從18265是 18263,由 vboxsync 提交於 16 年 前

Main,VBox/param.h: Move the hard RAM limit out of the schema.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 173.2 KB
 
1/* $Id: ApplianceImpl.cpp 18263 2009-03-25 16:26:37Z vboxsync $ */
2/** @file
3 *
4 * IAppliance and IVirtualSystem COM class implementations.
5 */
6
7/*
8 * Copyright (C) 2008-2009 Sun Microsystems, Inc.
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.alldomusa.eu.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
19 * Clara, CA 95054 USA or visit http://www.sun.com if you need
20 * additional information or have any questions.
21 */
22
23#include <VBox/param.h>
24#include <iprt/stream.h>
25#include <iprt/path.h>
26#include <iprt/dir.h>
27#include <iprt/file.h>
28
29#include "ApplianceImpl.h"
30#include "VirtualBoxImpl.h"
31#include "GuestOSTypeImpl.h"
32#include "ProgressImpl.h"
33#include "MachineImpl.h"
34#include "HostNetworkInterfaceImpl.h"
35
36#include "Logging.h"
37
38#include "VBox/xml.h"
39
40using namespace std;
41
42////////////////////////////////////////////////////////////////////////////////
43//
44// hardware definitions
45//
46////////////////////////////////////////////////////////////////////////////////
47
48struct DiskImage
49{
50 Utf8Str strDiskId; // value from DiskSection/Disk/@diskId
51 int64_t iCapacity; // value from DiskSection/Disk/@capacity;
52 // (maximum size for dynamic images, I guess; we always translate this to bytes)
53 int64_t iPopulatedSize; // value from DiskSection/Disk/@populatedSize
54 // (actual used size of disk, always in bytes; can be an estimate of used disk
55 // space, but cannot be larger than iCapacity)
56 Utf8Str strFormat; // value from DiskSection/Disk/@format
57 // typically http://www.vmware.com/specifications/vmdk.html#sparse
58
59 // fields from /References/File; the spec says the file reference from disk can be empty,
60 // so in that case, strFilename will be empty, then a new disk should be created
61 Utf8Str strHref; // value from /References/File/@href (filename); if empty, then the remaining fields are ignored
62 int64_t iSize; // value from /References/File/@size (optional according to spec; then we set -1 here)
63 int64_t iChunkSize; // value from /References/File/@chunkSize (optional, unsupported)
64 Utf8Str strCompression; // value from /References/File/@compression (optional, can be "gzip" according to spec)
65};
66
67struct VirtualHardwareItem
68{
69 Utf8Str strDescription;
70 Utf8Str strCaption;
71 Utf8Str strElementName;
72
73 uint32_t ulInstanceID;
74 uint32_t ulParent;
75
76 OVFResourceType_T resourceType;
77 Utf8Str strOtherResourceType;
78 Utf8Str strResourceSubType;
79
80 Utf8Str strHostResource; // "Abstractly specifies how a device shall connect to a resource on the deployment platform.
81 // Not all devices need a backing." Used with disk items, for which this references a virtual
82 // disk from the Disks section.
83 bool fAutomaticAllocation;
84 bool fAutomaticDeallocation;
85 Utf8Str strConnection; // "All Ethernet adapters that specify the same abstract network connection name within an OVF
86 // package shall be deployed on the same network. The abstract network connection name shall be
87 // listed in the NetworkSection at the outermost envelope level." We ignore this and only set up
88 // a network adapter depending on the network name.
89 Utf8Str strAddress; // "Device-specific. For an Ethernet adapter, this specifies the MAC address."
90 Utf8Str strAddressOnParent; // "For a device, this specifies its location on the controller."
91 Utf8Str strAllocationUnits; // "Specifies the units of allocation used. For example, “byte * 2^20”."
92 uint64_t ullVirtualQuantity; // "Specifies the quantity of resources presented. For example, “256”."
93 uint64_t ullReservation; // "Specifies the minimum quantity of resources guaranteed to be available."
94 uint64_t ullLimit; // "Specifies the maximum quantity of resources that will be granted."
95 uint64_t ullWeight; // "Specifies a relative priority for this allocation in relation to other allocations."
96
97 Utf8Str strConsumerVisibility;
98 Utf8Str strMappingBehavior;
99 Utf8Str strPoolID;
100 uint32_t ulBusNumber; // seen with IDE controllers, but not listed in OVF spec
101
102 uint32_t ulLineNumber; // line number of <Item> element in XML source; cached for error messages
103
104 VirtualHardwareItem()
105 : ulInstanceID(0), fAutomaticAllocation(false), fAutomaticDeallocation(false), ullVirtualQuantity(0), ullReservation(0), ullLimit(0), ullWeight(0), ulBusNumber(0), ulLineNumber(0)
106 {};
107};
108
109typedef map<Utf8Str, DiskImage> DiskImagesMap;
110
111struct VirtualSystem;
112
113typedef map<uint32_t, VirtualHardwareItem> HardwareItemsMap;
114
115struct HardDiskController
116{
117 uint32_t idController; // instance ID (Item/InstanceId); this gets referenced from HardDisk
118 enum ControllerSystemType { IDE, SATA, SCSI };
119 ControllerSystemType system; // one of IDE, SATA, SCSI
120 Utf8Str strControllerType; // controller subtype (Item/ResourceSubType); e.g. "LsiLogic"; can be empty (esp. for IDE)
121 Utf8Str strAddress; // for IDE
122 uint32_t ulBusNumber; // for IDE
123
124 HardDiskController()
125 : idController(0),
126 ulBusNumber(0)
127 {
128 }
129};
130
131typedef map<uint32_t, HardDiskController> ControllersMap;
132
133struct VirtualDisk
134{
135 uint32_t idController; // SCSI (or IDE) controller this disk is connected to;
136 // points into VirtualSystem.mapControllers
137 uint32_t ulAddressOnParent; // parsed strAddressOnParent of hardware item; will be 0 or 1 for IDE
138 // and possibly higher for disks attached to SCSI controllers (untested)
139 Utf8Str strDiskId; // if the hard disk has an ovf:/disk/<id> reference,
140 // this receives the <id> component; points to one of the
141 // references in Appliance::Data.mapDisks
142};
143
144typedef map<Utf8Str, VirtualDisk> VirtualDisksMap;
145
146struct EthernetAdapter
147{
148 Utf8Str strAdapterType; // "PCNet32" or "E1000" or whatever; from <rasd:ResourceSubType>
149 Utf8Str strNetworkName; // from <rasd:Connection>
150};
151
152typedef list<EthernetAdapter> EthernetAdaptersList;
153
154struct VirtualSystem
155{
156 Utf8Str strName; // copy of VirtualSystem/@id
157
158 Utf8Str strDescription; // copy of VirtualSystem/Info content
159
160 CIMOSType_T cimos;
161 Utf8Str strVirtualSystemType; // generic hardware description; OVF says this can be something like "vmx-4" or "xen";
162 // VMware Workstation 6.5 is "vmx-07"
163
164 HardwareItemsMap mapHardwareItems; // map of virtual hardware items, sorted by unique instance ID
165
166 uint64_t ullMemorySize; // always in bytes, copied from llHardwareItems; default = 0 (unspecified)
167 uint16_t cCPUs; // no. of CPUs, copied from llHardwareItems; default = 1
168
169 EthernetAdaptersList llEthernetAdapters; // (one for each VirtualSystem/Item[@ResourceType=10]element)
170
171 ControllersMap mapControllers;
172 // list of hard disk controllers
173 // (one for each VirtualSystem/Item[@ResourceType=6] element with accumulated data from children)
174
175 VirtualDisksMap mapVirtualDisks;
176 // (one for each VirtualSystem/Item[@ResourceType=17] element with accumulated data from children)
177
178 bool fHasFloppyDrive; // true if there's a floppy item in mapHardwareItems
179 bool fHasCdromDrive; // true if there's a CD-ROM item in mapHardwareItems; ISO images are not yet supported by OVFtool
180 bool fHasUsbController; // true if there's a USB controller item in mapHardwareItems
181
182 Utf8Str strSoundCardType; // if not empty, then the system wants a soundcard; this then specifies the hardware;
183 // VMware Workstation 6.5 uses "ensoniq1371" for example
184
185 Utf8Str strLicenseInfo; // license info if any; receives contents of VirtualSystem/EulaSection/Info
186 Utf8Str strLicenseText; // license info if any; receives contents of VirtualSystem/EulaSection/License
187
188 VirtualSystem()
189 : ullMemorySize(0), cCPUs(1), fHasFloppyDrive(false), fHasCdromDrive(false), fHasUsbController(false)
190 {
191 }
192};
193
194////////////////////////////////////////////////////////////////////////////////
195//
196// Appliance data definition
197//
198////////////////////////////////////////////////////////////////////////////////
199
200// opaque private instance data of Appliance class
201struct Appliance::Data
202{
203 Utf8Str strPath; // file name last given to either read() or write()
204
205 DiskImagesMap mapDisks; // map of DiskImage structs, sorted by DiskImage.strDiskId
206
207 list<VirtualSystem> llVirtualSystems; // list of virtual systems, created by and valid after read()
208
209 list< ComObjPtr<VirtualSystemDescription> > virtualSystemDescriptions; //
210
211 list<Utf8Str> llWarnings;
212};
213
214struct VirtualSystemDescription::Data
215{
216 list<VirtualSystemDescriptionEntry> llDescriptions;
217};
218
219////////////////////////////////////////////////////////////////////////////////
220//
221// internal helpers
222//
223////////////////////////////////////////////////////////////////////////////////
224
225static Utf8Str stripFilename(const Utf8Str &strFile)
226{
227 Utf8Str str2(strFile);
228 RTPathStripFilename(str2.mutableRaw());
229 return str2;
230}
231
232static const struct
233{
234 CIMOSType_T cim;
235 const char *pcszVbox;
236}
237 g_osTypes[] =
238 {
239 { CIMOSType_CIMOS_Unknown, SchemaDefs_OSTypeId_Other },
240 { CIMOSType_CIMOS_OS2, SchemaDefs_OSTypeId_OS2 },
241 { CIMOSType_CIMOS_MSDOS, SchemaDefs_OSTypeId_DOS },
242 { CIMOSType_CIMOS_WIN3x, SchemaDefs_OSTypeId_Windows31 },
243 { CIMOSType_CIMOS_WIN95, SchemaDefs_OSTypeId_Windows95 },
244 { CIMOSType_CIMOS_WIN98, SchemaDefs_OSTypeId_Windows98 },
245 { CIMOSType_CIMOS_WINNT, SchemaDefs_OSTypeId_WindowsNT4 },
246 { CIMOSType_CIMOS_NetWare, SchemaDefs_OSTypeId_Netware },
247 { CIMOSType_CIMOS_NovellOES, SchemaDefs_OSTypeId_Netware },
248 { CIMOSType_CIMOS_Solaris, SchemaDefs_OSTypeId_OpenSolaris },
249 { CIMOSType_CIMOS_SunOS, SchemaDefs_OSTypeId_OpenSolaris },
250 { CIMOSType_CIMOS_FreeBSD, SchemaDefs_OSTypeId_FreeBSD },
251 { CIMOSType_CIMOS_NetBSD, SchemaDefs_OSTypeId_NetBSD },
252 { CIMOSType_CIMOS_QNX, SchemaDefs_OSTypeId_QNX },
253 { CIMOSType_CIMOS_Windows2000, SchemaDefs_OSTypeId_Windows2000 },
254 { CIMOSType_CIMOS_WindowsMe, SchemaDefs_OSTypeId_WindowsMe },
255 { CIMOSType_CIMOS_OpenBSD, SchemaDefs_OSTypeId_OpenBSD },
256 { CIMOSType_CIMOS_WindowsXP, SchemaDefs_OSTypeId_WindowsXP },
257 { CIMOSType_CIMOS_WindowsXPEmbedded, SchemaDefs_OSTypeId_WindowsXP },
258 { CIMOSType_CIMOS_WindowsEmbeddedforPointofService, SchemaDefs_OSTypeId_WindowsXP },
259 { CIMOSType_CIMOS_MicrosoftWindowsServer2003, SchemaDefs_OSTypeId_Windows2003 },
260 { CIMOSType_CIMOS_MicrosoftWindowsServer2003_64, SchemaDefs_OSTypeId_Windows2003_64 },
261 { CIMOSType_CIMOS_WindowsXP_64, SchemaDefs_OSTypeId_WindowsXP_64 },
262 { CIMOSType_CIMOS_WindowsVista, SchemaDefs_OSTypeId_WindowsVista },
263 { CIMOSType_CIMOS_WindowsVista_64, SchemaDefs_OSTypeId_WindowsVista_64 },
264 { CIMOSType_CIMOS_MicrosoftWindowsServer2008, SchemaDefs_OSTypeId_Windows2008 },
265 { CIMOSType_CIMOS_MicrosoftWindowsServer2008_64, SchemaDefs_OSTypeId_Windows2008_64 },
266 { CIMOSType_CIMOS_FreeBSD_64, SchemaDefs_OSTypeId_FreeBSD_64 },
267 { CIMOSType_CIMOS_RedHatEnterpriseLinux, SchemaDefs_OSTypeId_RedHat },
268 { CIMOSType_CIMOS_RedHatEnterpriseLinux_64, SchemaDefs_OSTypeId_RedHat_64 },
269 { CIMOSType_CIMOS_Solaris_64, SchemaDefs_OSTypeId_OpenSolaris_64 },
270 { CIMOSType_CIMOS_SUSE, SchemaDefs_OSTypeId_OpenSUSE },
271 { CIMOSType_CIMOS_SLES, SchemaDefs_OSTypeId_OpenSUSE },
272 { CIMOSType_CIMOS_NovellLinuxDesktop, SchemaDefs_OSTypeId_OpenSUSE },
273 { CIMOSType_CIMOS_SUSE_64, SchemaDefs_OSTypeId_OpenSUSE_64 },
274 { CIMOSType_CIMOS_SLES_64, SchemaDefs_OSTypeId_OpenSUSE_64 },
275 { CIMOSType_CIMOS_LINUX, SchemaDefs_OSTypeId_Linux },
276 { CIMOSType_CIMOS_SunJavaDesktopSystem, SchemaDefs_OSTypeId_Linux },
277 { CIMOSType_CIMOS_TurboLinux, SchemaDefs_OSTypeId_Linux},
278
279 // { CIMOSType_CIMOS_TurboLinux_64, },
280 // { CIMOSType_CIMOS_Linux_64, },
281 // osTypeVBox = VBOXOSTYPE_Linux_x64;
282 // break;
283
284 { CIMOSType_CIMOS_Mandriva, SchemaDefs_OSTypeId_Mandriva },
285 { CIMOSType_CIMOS_Mandriva_64, SchemaDefs_OSTypeId_Mandriva_64 },
286 { CIMOSType_CIMOS_Ubuntu, SchemaDefs_OSTypeId_Ubuntu },
287 { CIMOSType_CIMOS_Ubuntu_64, SchemaDefs_OSTypeId_Ubuntu_64 },
288 { CIMOSType_CIMOS_Debian, SchemaDefs_OSTypeId_Debian },
289 { CIMOSType_CIMOS_Debian_64, SchemaDefs_OSTypeId_Debian_64 },
290 { CIMOSType_CIMOS_Linux_2_4_x, SchemaDefs_OSTypeId_Linux24 },
291 { CIMOSType_CIMOS_Linux_2_4_x_64, SchemaDefs_OSTypeId_Linux24_64 },
292 { CIMOSType_CIMOS_Linux_2_6_x, SchemaDefs_OSTypeId_Linux26 },
293 { CIMOSType_CIMOS_Linux_2_6_x_64, SchemaDefs_OSTypeId_Linux26_64 }
294};
295
296/**
297 * Private helper func that suggests a VirtualBox guest OS type
298 * for the given OVF operating system type.
299 * @param osTypeVBox
300 * @param c
301 */
302static void convertCIMOSType2VBoxOSType(Utf8Str &strType, CIMOSType_T c)
303{
304 for (size_t i = 0; i < RT_ELEMENTS(g_osTypes); ++i)
305 {
306 if (c == g_osTypes[i].cim)
307 {
308 strType = g_osTypes[i].pcszVbox;
309 return;
310 }
311 }
312
313 strType = SchemaDefs_OSTypeId_Other;
314}
315
316/**
317 * Private helper func that suggests a VirtualBox guest OS type
318 * for the given OVF operating system type.
319 * @param osTypeVBox
320 * @param c
321 */
322static CIMOSType_T convertVBoxOSType2CIMOSType(const char *pcszVbox)
323{
324 for (size_t i = 0; i < RT_ELEMENTS(g_osTypes); ++i)
325 {
326 if (!RTStrICmp(pcszVbox, g_osTypes[i].pcszVbox))
327 return g_osTypes[i].cim;
328 }
329
330 return CIMOSType_CIMOS_Other;
331}
332
333////////////////////////////////////////////////////////////////////////////////
334//
335// IVirtualBox public methods
336//
337////////////////////////////////////////////////////////////////////////////////
338
339// This code is here so we won't have to include the appliance headers in the
340// IVirtualBox implementation.
341
342/**
343 * Implementation for IVirtualBox::createAppliance.
344 *
345 * @param anAppliance IAppliance object created if S_OK is returned.
346 * @return S_OK or error.
347 */
348STDMETHODIMP VirtualBox::CreateAppliance(IAppliance** anAppliance)
349{
350 HRESULT rc;
351
352 ComObjPtr<Appliance> appliance;
353 appliance.createObject();
354 rc = appliance->init(this);
355
356 if (SUCCEEDED(rc))
357 appliance.queryInterfaceTo(anAppliance);
358
359 return rc;
360}
361
362////////////////////////////////////////////////////////////////////////////////
363//
364// Appliance constructor / destructor
365//
366////////////////////////////////////////////////////////////////////////////////
367
368DEFINE_EMPTY_CTOR_DTOR(Appliance)
369struct shutup {};
370
371/**
372 * Appliance COM initializer.
373 * @param
374 * @return
375 */
376
377HRESULT Appliance::init(VirtualBox *aVirtualBox)
378{
379 /* Enclose the state transition NotReady->InInit->Ready */
380 AutoInitSpan autoInitSpan(this);
381 AssertReturn(autoInitSpan.isOk(), E_FAIL);
382
383 /* Weak reference to a VirtualBox object */
384 unconst(mVirtualBox) = aVirtualBox;
385
386 // initialize data
387 m = new Data;
388
389 /* Confirm a successful initialization */
390 autoInitSpan.setSucceeded();
391
392 return S_OK;
393}
394
395/**
396 * Appliance COM uninitializer.
397 * @return
398 */
399void Appliance::uninit()
400{
401 delete m;
402 m = NULL;
403}
404
405////////////////////////////////////////////////////////////////////////////////
406//
407// Appliance private methods
408//
409////////////////////////////////////////////////////////////////////////////////
410
411/**
412 * Private helper method that goes thru the elements of the given "current" element in the OVF XML
413 * and handles the contained child elements (which can be "Section" or "Content" elements).
414 *
415 * @param pcszPath Path spec of the XML file, for error messages.
416 * @param pReferencesElement "References" element from OVF, for looking up file specifications; can be NULL if no such element is present.
417 * @param pCurElem Element whose children are to be analyzed here.
418 * @return
419 */
420HRESULT Appliance::LoopThruSections(const char *pcszPath,
421 const xml::ElementNode *pReferencesElem,
422 const xml::ElementNode *pCurElem)
423{
424 HRESULT rc;
425
426 xml::NodesLoop loopChildren(*pCurElem);
427 const xml::ElementNode *pElem;
428 while ((pElem = loopChildren.forAllNodes()))
429 {
430 const char *pcszElemName = pElem->getName();
431 const char *pcszTypeAttr = "";
432 const xml::AttributeNode *pTypeAttr;
433 if ((pTypeAttr = pElem->findAttribute("type")))
434 pcszTypeAttr = pTypeAttr->getValue();
435
436 if ( (!strcmp(pcszElemName, "DiskSection"))
437 || ( (!strcmp(pcszElemName, "Section"))
438 && (!strcmp(pcszTypeAttr, "ovf:DiskSection_Type"))
439 )
440 )
441 {
442 if (!(SUCCEEDED((rc = HandleDiskSection(pcszPath, pReferencesElem, pElem)))))
443 return rc;
444 }
445 else if ( (!strcmp(pcszElemName, "NetworkSection"))
446 || ( (!strcmp(pcszElemName, "Section"))
447 && (!strcmp(pcszTypeAttr, "ovf:NetworkSection_Type"))
448 )
449 )
450 {
451 if (!(SUCCEEDED((rc = HandleNetworkSection(pcszPath, pElem)))))
452 return rc;
453 }
454 else if ( (!strcmp(pcszElemName, "DeploymentOptionSection")))
455 {
456 // TODO
457 }
458 else if ( (!strcmp(pcszElemName, "Info")))
459 {
460 // child of VirtualSystemCollection -- TODO
461 }
462 else if ( (!strcmp(pcszElemName, "ResourceAllocationSection")))
463 {
464 // child of VirtualSystemCollection -- TODO
465 }
466 else if ( (!strcmp(pcszElemName, "StartupSection")))
467 {
468 // child of VirtualSystemCollection -- TODO
469 }
470 else if ( (!strcmp(pcszElemName, "VirtualSystem"))
471 || ( (!strcmp(pcszElemName, "Content"))
472 && (!strcmp(pcszTypeAttr, "ovf:VirtualSystem_Type"))
473 )
474 )
475 {
476 if (!(SUCCEEDED((rc = HandleVirtualSystemContent(pcszPath, pElem)))))
477 return rc;
478 }
479 else if ( (!strcmp(pcszElemName, "VirtualSystemCollection"))
480 || ( (!strcmp(pcszElemName, "Content"))
481 && (!strcmp(pcszTypeAttr, "ovf:VirtualSystemCollection_Type"))
482 )
483 )
484 {
485 // TODO ResourceAllocationSection
486
487 // recurse for this, since it has VirtualSystem elements as children
488 if (!(SUCCEEDED((rc = LoopThruSections(pcszPath, pReferencesElem, pElem)))))
489 return rc;
490 }
491 }
492
493 return S_OK;
494}
495
496/**
497 * Private helper method that handles disk sections in the OVF XML.
498 * Gets called indirectly from IAppliance::read().
499 *
500 * @param pcszPath Path spec of the XML file, for error messages.
501 * @param pReferencesElement "References" element from OVF, for looking up file specifications; can be NULL if no such element is present.
502 * @param pSectionElem Section element for which this helper is getting called.
503 * @return
504 */
505HRESULT Appliance::HandleDiskSection(const char *pcszPath,
506 const xml::ElementNode *pReferencesElem,
507 const xml::ElementNode *pSectionElem)
508{
509 // contains "Disk" child elements
510 xml::NodesLoop loopDisks(*pSectionElem, "Disk");
511 const xml::ElementNode *pelmDisk;
512 while ((pelmDisk = loopDisks.forAllNodes()))
513 {
514 DiskImage d;
515 const char *pcszBad = NULL;
516 if (!(pelmDisk->getAttributeValue("diskId", d.strDiskId)))
517 pcszBad = "diskId";
518 else if (!(pelmDisk->getAttributeValue("format", d.strFormat)))
519 pcszBad = "format";
520 else if (!(pelmDisk->getAttributeValue("capacity", d.iCapacity)))
521 pcszBad = "capacity";
522 else
523 {
524 if (!(pelmDisk->getAttributeValue("populatedSize", d.iPopulatedSize)))
525 // optional
526 d.iPopulatedSize = -1;
527
528 Utf8Str strFileRef;
529 if (pelmDisk->getAttributeValue("fileRef", strFileRef)) // optional
530 {
531 // look up corresponding /References/File nodes (list built above)
532 const xml::ElementNode *pFileElem;
533 if ( pReferencesElem
534 && ((pFileElem = pReferencesElem->findChildElementFromId(strFileRef.c_str())))
535 )
536 {
537 // copy remaining values from file node then
538 const char *pcszBadInFile = NULL;
539 if (!(pFileElem->getAttributeValue("href", d.strHref)))
540 pcszBadInFile = "href";
541 else if (!(pFileElem->getAttributeValue("size", d.iSize)))
542 d.iSize = -1; // optional
543 // if (!(pFileElem->getAttributeValue("size", d.iChunkSize))) TODO
544 d.iChunkSize = -1; // optional
545 pFileElem->getAttributeValue("compression", d.strCompression);
546
547 if (pcszBadInFile)
548 return setError(VBOX_E_FILE_ERROR,
549 tr("Error reading \"%s\": missing or invalid attribute '%s' in 'File' element, line %d"),
550 pcszPath,
551 pcszBadInFile,
552 pFileElem->getLineNumber());
553 }
554 else
555 return setError(VBOX_E_FILE_ERROR,
556 tr("Error reading \"%s\": cannot find References/File element for ID '%s' referenced by 'Disk' element, line %d"),
557 pcszPath,
558 strFileRef.c_str(),
559 pelmDisk->getLineNumber());
560 }
561 }
562
563 if (pcszBad)
564 return setError(VBOX_E_FILE_ERROR,
565 tr("Error reading \"%s\": missing or invalid attribute '%s' in 'DiskSection' element, line %d"),
566 pcszPath,
567 pcszBad,
568 pelmDisk->getLineNumber());
569
570 m->mapDisks[d.strDiskId] = d;
571 }
572
573 return S_OK;
574}
575
576/**
577 * Private helper method that handles network sections in the OVF XML.
578 * Gets called indirectly from IAppliance::read().
579 *
580 * @param pcszPath Path spec of the XML file, for error messages.
581 * @param pSectionElem Section element for which this helper is getting called.
582 * @return
583 */
584HRESULT Appliance::HandleNetworkSection(const char * /* pcszPath */,
585 const xml::ElementNode * /* pSectionElem */)
586{
587 // we ignore network sections for now
588
589// xml::NodesLoop loopNetworks(*pSectionElem, "Network");
590// const xml::Node *pelmNetwork;
591// while ((pelmNetwork = loopNetworks.forAllNodes()))
592// {
593// Network n;
594// if (!(pelmNetwork->getAttributeValue("name", n.strNetworkName)))
595// return setError(VBOX_E_FILE_ERROR,
596// tr("Error reading \"%s\": missing 'name' attribute in 'Network', line %d"),
597// pcszPath,
598// pelmNetwork->getLineNumber());
599//
600// m->mapNetworks[n.strNetworkName] = n;
601// }
602
603 return S_OK;
604}
605
606/**
607 * Private helper method that handles a "VirtualSystem" element in the OVF XML.
608 * Gets called indirectly from IAppliance::read().
609 *
610 * @param pcszPath
611 * @param pContentElem
612 * @return
613 */
614HRESULT Appliance::HandleVirtualSystemContent(const char *pcszPath,
615 const xml::ElementNode *pelmVirtualSystem)
616{
617 VirtualSystem vsys;
618
619 const xml::AttributeNode *pIdAttr = pelmVirtualSystem->findAttribute("id");
620 if (pIdAttr)
621 vsys.strName = pIdAttr->getValue();
622
623 xml::NodesLoop loop(*pelmVirtualSystem); // all child elements
624 const xml::ElementNode *pelmThis;
625 while ((pelmThis = loop.forAllNodes()))
626 {
627 const char *pcszElemName = pelmThis->getName();
628 const xml::AttributeNode *pTypeAttr = pelmThis->findAttribute("type");
629 const char *pcszTypeAttr = (pTypeAttr) ? pTypeAttr->getValue() : "";
630
631 if ( (!strcmp(pcszElemName, "EulaSection"))
632 || (!strcmp(pcszTypeAttr, "ovf:EulaSection_Type"))
633 )
634 {
635 /* <EulaSection>
636 <Info ovf:msgid="6">License agreement for the Virtual System.</Info>
637 <License ovf:msgid="1">License terms can go in here.</License>
638 </EulaSection> */
639
640 const xml::ElementNode *pelmInfo, *pelmLicense;
641 if ( ((pelmInfo = pelmThis->findChildElement("Info")))
642 && ((pelmLicense = pelmThis->findChildElement("License")))
643 )
644 {
645 vsys.strLicenseInfo = pelmInfo->getValue();
646 vsys.strLicenseText = pelmLicense->getValue();
647 }
648 }
649 else if ( (!strcmp(pcszElemName, "VirtualHardwareSection"))
650 || (!strcmp(pcszTypeAttr, "ovf:VirtualHardwareSection_Type"))
651 )
652 {
653 const xml::ElementNode *pelmSystem, *pelmVirtualSystemType;
654 if ((pelmSystem = pelmThis->findChildElement("System")))
655 {
656 /* <System>
657 <vssd:Description>Description of the virtual hardware section.</vssd:Description>
658 <vssd:ElementName>vmware</vssd:ElementName>
659 <vssd:InstanceID>1</vssd:InstanceID>
660 <vssd:VirtualSystemIdentifier>MyLampService</vssd:VirtualSystemIdentifier>
661 <vssd:VirtualSystemType>vmx-4</vssd:VirtualSystemType>
662 </System>*/
663 if ((pelmVirtualSystemType = pelmSystem->findChildElement("VirtualSystemType")))
664 vsys.strVirtualSystemType = pelmVirtualSystemType->getValue();
665 }
666
667 xml::NodesLoop loopVirtualHardwareItems(*pelmThis, "Item"); // all "Item" child elements
668 const xml::ElementNode *pelmItem;
669 while ((pelmItem = loopVirtualHardwareItems.forAllNodes()))
670 {
671 VirtualHardwareItem i;
672
673 i.ulLineNumber = pelmItem->getLineNumber();
674
675 xml::NodesLoop loopItemChildren(*pelmItem); // all child elements
676 const xml::ElementNode *pelmItemChild;
677 while ((pelmItemChild = loopItemChildren.forAllNodes()))
678 {
679 const char *pcszItemChildName = pelmItemChild->getName();
680 if (!strcmp(pcszItemChildName, "Description"))
681 i.strDescription = pelmItemChild->getValue();
682 else if (!strcmp(pcszItemChildName, "Caption"))
683 i.strCaption = pelmItemChild->getValue();
684 else if (!strcmp(pcszItemChildName, "ElementName"))
685 i.strElementName = pelmItemChild->getValue();
686 else if ( (!strcmp(pcszItemChildName, "InstanceID"))
687 || (!strcmp(pcszItemChildName, "InstanceId"))
688 )
689 pelmItemChild->copyValue(i.ulInstanceID);
690 else if (!strcmp(pcszItemChildName, "HostResource"))
691 i.strHostResource = pelmItemChild->getValue();
692 else if (!strcmp(pcszItemChildName, "ResourceType"))
693 {
694 uint32_t ulType;
695 pelmItemChild->copyValue(ulType);
696 i.resourceType = (OVFResourceType_T)ulType;
697 }
698 else if (!strcmp(pcszItemChildName, "OtherResourceType"))
699 i.strOtherResourceType = pelmItemChild->getValue();
700 else if (!strcmp(pcszItemChildName, "ResourceSubType"))
701 i.strResourceSubType = pelmItemChild->getValue();
702 else if (!strcmp(pcszItemChildName, "AutomaticAllocation"))
703 i.fAutomaticAllocation = (!strcmp(pelmItemChild->getValue(), "true")) ? true : false;
704 else if (!strcmp(pcszItemChildName, "AutomaticDeallocation"))
705 i.fAutomaticDeallocation = (!strcmp(pelmItemChild->getValue(), "true")) ? true : false;
706 else if (!strcmp(pcszItemChildName, "Parent"))
707 pelmItemChild->copyValue(i.ulParent);
708 else if (!strcmp(pcszItemChildName, "Connection"))
709 i.strConnection = pelmItemChild->getValue();
710 else if (!strcmp(pcszItemChildName, "Address"))
711 i.strAddress = pelmItemChild->getValue();
712 else if (!strcmp(pcszItemChildName, "AddressOnParent"))
713 i.strAddressOnParent = pelmItemChild->getValue();
714 else if (!strcmp(pcszItemChildName, "AllocationUnits"))
715 i.strAllocationUnits = pelmItemChild->getValue();
716 else if (!strcmp(pcszItemChildName, "VirtualQuantity"))
717 pelmItemChild->copyValue(i.ullVirtualQuantity);
718 else if (!strcmp(pcszItemChildName, "Reservation"))
719 pelmItemChild->copyValue(i.ullReservation);
720 else if (!strcmp(pcszItemChildName, "Limit"))
721 pelmItemChild->copyValue(i.ullLimit);
722 else if (!strcmp(pcszItemChildName, "Weight"))
723 pelmItemChild->copyValue(i.ullWeight);
724 else if (!strcmp(pcszItemChildName, "ConsumerVisibility"))
725 i.strConsumerVisibility = pelmItemChild->getValue();
726 else if (!strcmp(pcszItemChildName, "MappingBehavior"))
727 i.strMappingBehavior = pelmItemChild->getValue();
728 else if (!strcmp(pcszItemChildName, "PoolID"))
729 i.strPoolID = pelmItemChild->getValue();
730 else if (!strcmp(pcszItemChildName, "BusNumber"))
731 pelmItemChild->copyValue(i.ulBusNumber);
732 else
733 return setError(VBOX_E_FILE_ERROR,
734 tr("Error reading \"%s\": unknown element \"%s\" under Item element, line %d"),
735 pcszPath,
736 pcszItemChildName,
737 i.ulLineNumber);
738 }
739
740 // store!
741 vsys.mapHardwareItems[i.ulInstanceID] = i;
742 }
743
744 // now go thru all hardware items and handle them according to their type;
745 // in this first loop we handle all items _except_ hard disk images,
746 // which we'll handle in a second loop below
747 HardwareItemsMap::const_iterator itH;
748 for (itH = vsys.mapHardwareItems.begin();
749 itH != vsys.mapHardwareItems.end();
750 ++itH)
751 {
752 const VirtualHardwareItem &i = itH->second;
753
754 // do some analysis
755 switch (i.resourceType)
756 {
757 case OVFResourceType_Processor: // 3
758 /* <rasd:Caption>1 virtual CPU</rasd:Caption>
759 <rasd:Description>Number of virtual CPUs</rasd:Description>
760 <rasd:ElementName>virtual CPU</rasd:ElementName>
761 <rasd:InstanceID>1</rasd:InstanceID>
762 <rasd:ResourceType>3</rasd:ResourceType>
763 <rasd:VirtualQuantity>1</rasd:VirtualQuantity>*/
764 if (i.ullVirtualQuantity < UINT16_MAX)
765 vsys.cCPUs = (uint16_t)i.ullVirtualQuantity;
766 else
767 return setError(VBOX_E_FILE_ERROR,
768 tr("Error reading \"%s\": CPU count %RI64 is larger than %d, line %d"),
769 pcszPath,
770 i.ullVirtualQuantity,
771 UINT16_MAX,
772 i.ulLineNumber);
773 break;
774
775 case OVFResourceType_Memory: // 4
776 if ( (i.strAllocationUnits == "MegaBytes") // found in OVF created by OVF toolkit
777 || (i.strAllocationUnits == "MB") // found in MS docs
778 || (i.strAllocationUnits == "byte * 2^20") // suggested by OVF spec DSP0243 page 21
779 )
780 vsys.ullMemorySize = i.ullVirtualQuantity * 1024 * 1024;
781 else
782 return setError(VBOX_E_FILE_ERROR,
783 tr("Error reading \"%s\": Invalid allocation unit \"%s\" specified with memory size item, line %d"),
784 pcszPath,
785 i.strAllocationUnits.c_str(),
786 i.ulLineNumber);
787 break;
788
789 case OVFResourceType_IDEController: // 5
790 {
791 /* <Item>
792 <rasd:Caption>ideController0</rasd:Caption>
793 <rasd:Description>IDE Controller</rasd:Description>
794 <rasd:InstanceId>5</rasd:InstanceId>
795 <rasd:ResourceType>5</rasd:ResourceType>
796 <rasd:Address>0</rasd:Address>
797 <rasd:BusNumber>0</rasd:BusNumber>
798 </Item> */
799 HardDiskController hdc;
800 hdc.system = HardDiskController::IDE;
801 hdc.idController = i.ulInstanceID;
802 hdc.strAddress = i.strAddress;
803 hdc.ulBusNumber = i.ulBusNumber;
804
805 vsys.mapControllers[i.ulInstanceID] = hdc;
806 }
807 break;
808
809 case OVFResourceType_ParallelSCSIHBA: // 6 SCSI controller
810 {
811 /* <Item>
812 <rasd:Caption>SCSI Controller 0 - LSI Logic</rasd:Caption>
813 <rasd:Description>SCI Controller</rasd:Description>
814 <rasd:ElementName>SCSI controller</rasd:ElementName>
815 <rasd:InstanceID>4</rasd:InstanceID>
816 <rasd:ResourceSubType>LsiLogic</rasd:ResourceSubType>
817 <rasd:ResourceType>6</rasd:ResourceType>
818 </Item> */
819 HardDiskController hdc;
820 hdc.system = HardDiskController::SCSI;
821 hdc.idController = i.ulInstanceID;
822 hdc.strControllerType = i.strResourceSubType;
823
824 vsys.mapControllers[i.ulInstanceID] = hdc;
825 }
826 break;
827
828 case OVFResourceType_EthernetAdapter: // 10
829 {
830 /* <Item>
831 <rasd:Caption>Ethernet adapter on 'Bridged'</rasd:Caption>
832 <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
833 <rasd:Connection>Bridged</rasd:Connection>
834 <rasd:InstanceID>6</rasd:InstanceID>
835 <rasd:ResourceType>10</rasd:ResourceType>
836 <rasd:ResourceSubType>E1000</rasd:ResourceSubType>
837 </Item>
838
839 OVF spec DSP 0243 page 21:
840 "For an Ethernet adapter, this specifies the abstract network connection name
841 for the virtual machine. All Ethernet adapters that specify the same abstract
842 network connection name within an OVF package shall be deployed on the same
843 network. The abstract network connection name shall be listed in the NetworkSection
844 at the outermost envelope level." */
845
846 // only store the name
847 EthernetAdapter ea;
848 ea.strAdapterType = i.strResourceSubType;
849 ea.strNetworkName = i.strConnection;
850 vsys.llEthernetAdapters.push_back(ea);
851 }
852 break;
853
854 case OVFResourceType_FloppyDrive: // 14
855 vsys.fHasFloppyDrive = true; // we have no additional information
856 break;
857
858 case OVFResourceType_CDDrive: // 15
859 /* <Item ovf:required="false">
860 <rasd:Caption>cdrom1</rasd:Caption>
861 <rasd:InstanceId>7</rasd:InstanceId>
862 <rasd:ResourceType>15</rasd:ResourceType>
863 <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
864 <rasd:Parent>5</rasd:Parent>
865 <rasd:AddressOnParent>0</rasd:AddressOnParent>
866 </Item> */
867 // I tried to see what happens if I set an ISO for the CD-ROM in VMware Workstation,
868 // but then the ovftool dies with "Device backing not supported". So I guess if
869 // VMware can't export ISOs, then we don't need to be able to import them right now.
870 vsys.fHasCdromDrive = true; // we have no additional information
871 break;
872
873 case OVFResourceType_HardDisk: // 17
874 // handled separately in second loop below
875 break;
876
877 case OVFResourceType_OtherStorageDevice: // 20 SATA controller
878 {
879 /* <Item>
880 <rasd:Description>SATA Controller</rasd:Description>
881 <rasd:Caption>sataController0</rasd:Caption>
882 <rasd:InstanceID>4</rasd:InstanceID>
883 <rasd:ResourceType>20</rasd:ResourceType>
884 <rasd:ResourceSubType>AHCI</rasd:ResourceSubType>
885 <rasd:Address>0</rasd:Address>
886 <rasd:BusNumber>0</rasd:BusNumber>
887 </Item> */
888 if (i.strCaption.startsWith ("sataController", Utf8Str::CaseInsensitive) &&
889 !i.strResourceSubType.compare ("AHCI", Utf8Str::CaseInsensitive))
890 {
891 HardDiskController hdc;
892 hdc.system = HardDiskController::SATA;
893 hdc.idController = i.ulInstanceID;
894 hdc.strControllerType = i.strResourceSubType;
895
896 vsys.mapControllers[i.ulInstanceID] = hdc;
897 }
898 else
899 return setError(VBOX_E_FILE_ERROR,
900 tr("Error reading \"%s\": Host resource of type \"Other Storage Device (%d)\" is supported with SATA AHCI controllers only, line %d"),
901 pcszPath,
902 OVFResourceType_OtherStorageDevice,
903 i.ulLineNumber);
904 }
905 break;
906
907 case OVFResourceType_USBController: // 23
908 /* <Item ovf:required="false">
909 <rasd:Caption>usb</rasd:Caption>
910 <rasd:Description>USB Controller</rasd:Description>
911 <rasd:InstanceId>3</rasd:InstanceId>
912 <rasd:ResourceType>23</rasd:ResourceType>
913 <rasd:Address>0</rasd:Address>
914 <rasd:BusNumber>0</rasd:BusNumber>
915 </Item> */
916 vsys.fHasUsbController = true; // we have no additional information
917 break;
918
919 case OVFResourceType_SoundCard: // 35
920 /* <Item ovf:required="false">
921 <rasd:Caption>sound</rasd:Caption>
922 <rasd:Description>Sound Card</rasd:Description>
923 <rasd:InstanceId>10</rasd:InstanceId>
924 <rasd:ResourceType>35</rasd:ResourceType>
925 <rasd:ResourceSubType>ensoniq1371</rasd:ResourceSubType>
926 <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
927 <rasd:AddressOnParent>3</rasd:AddressOnParent>
928 </Item> */
929 vsys.strSoundCardType = i.strResourceSubType;
930 break;
931
932 default:
933 return setError(VBOX_E_FILE_ERROR,
934 tr("Error reading \"%s\": Unknown resource type %d in hardware item, line %d"),
935 pcszPath,
936 i.resourceType,
937 i.ulLineNumber);
938 } // end switch
939 }
940
941 // now run through the items for a second time, but handle only
942 // hard disk images; otherwise the code would fail if a hard
943 // disk image appears in the OVF before its hard disk controller
944 for (itH = vsys.mapHardwareItems.begin();
945 itH != vsys.mapHardwareItems.end();
946 ++itH)
947 {
948 const VirtualHardwareItem &i = itH->second;
949
950 // do some analysis
951 switch (i.resourceType)
952 {
953 case OVFResourceType_HardDisk: // 17
954 {
955 /* <Item>
956 <rasd:Caption>Harddisk 1</rasd:Caption>
957 <rasd:Description>HD</rasd:Description>
958 <rasd:ElementName>Hard Disk</rasd:ElementName>
959 <rasd:HostResource>ovf://disk/lamp</rasd:HostResource>
960 <rasd:InstanceID>5</rasd:InstanceID>
961 <rasd:Parent>4</rasd:Parent>
962 <rasd:ResourceType>17</rasd:ResourceType>
963 </Item> */
964
965 // look up the hard disk controller element whose InstanceID equals our Parent;
966 // this is how the connection is specified in OVF
967 ControllersMap::const_iterator it = vsys.mapControllers.find(i.ulParent);
968 if (it == vsys.mapControllers.end())
969 return setError(VBOX_E_FILE_ERROR,
970 tr("Error reading \"%s\": Hard disk item with instance ID %d specifies invalid parent %d, line %d"),
971 pcszPath,
972 i.ulInstanceID,
973 i.ulParent,
974 i.ulLineNumber);
975 //const HardDiskController &hdc = it->second;
976
977 VirtualDisk vd;
978 vd.idController = i.ulParent;
979 i.strAddressOnParent.toInt(vd.ulAddressOnParent);
980 // ovf://disk/lamp
981 // 123456789012345
982 if (i.strHostResource.substr(0, 11) == "ovf://disk/")
983 vd.strDiskId = i.strHostResource.substr(11);
984 else if (i.strHostResource.substr(0, 6) == "/disk/")
985 vd.strDiskId = i.strHostResource.substr(6);
986
987 if ( !(vd.strDiskId.length())
988 || (m->mapDisks.find(vd.strDiskId) == m->mapDisks.end())
989 )
990 return setError(VBOX_E_FILE_ERROR,
991 tr("Error reading \"%s\": Hard disk item with instance ID %d specifies invalid host resource \"%s\", line %d"),
992 pcszPath,
993 i.ulInstanceID,
994 i.strHostResource.c_str(),
995 i.ulLineNumber);
996
997 vsys.mapVirtualDisks[vd.strDiskId] = vd;
998 }
999 break;
1000 }
1001 }
1002 }
1003 else if ( (!strcmp(pcszElemName, "OperatingSystemSection"))
1004 || (!strcmp(pcszTypeAttr, "ovf:OperatingSystemSection_Type"))
1005 )
1006 {
1007 uint64_t cimos64;
1008 if (!(pelmThis->getAttributeValue("id", cimos64)))
1009 return setError(VBOX_E_FILE_ERROR,
1010 tr("Error reading \"%s\": missing or invalid 'ovf:id' attribute in operating system section element, line %d"),
1011 pcszPath,
1012 pelmThis->getLineNumber());
1013
1014 vsys.cimos = (CIMOSType_T)cimos64;
1015 }
1016 else if ( (!strcmp(pcszElemName, "AnnotationSection"))
1017 || (!strcmp(pcszTypeAttr, "ovf:AnnotationSection_Type"))
1018 )
1019 {
1020 const xml::ElementNode *pelmAnnotation;
1021 if ((pelmAnnotation = pelmThis->findChildElement("Annotation")))
1022 vsys.strDescription = pelmAnnotation->getValue();
1023 }
1024 }
1025
1026 // now create the virtual system
1027 m->llVirtualSystems.push_back(vsys);
1028
1029 return S_OK;
1030}
1031
1032////////////////////////////////////////////////////////////////////////////////
1033//
1034// IAppliance public methods
1035//
1036////////////////////////////////////////////////////////////////////////////////
1037
1038/**
1039 * Public method implementation.
1040 * @param
1041 * @return
1042 */
1043STDMETHODIMP Appliance::COMGETTER(Path)(BSTR *aPath)
1044{
1045 if (!aPath)
1046 return E_POINTER;
1047
1048 AutoCaller autoCaller(this);
1049 CheckComRCReturnRC(autoCaller.rc());
1050
1051 AutoReadLock alock(this);
1052
1053 Bstr bstrPath(m->strPath);
1054 bstrPath.cloneTo(aPath);
1055
1056 return S_OK;
1057}
1058
1059/**
1060 * Public method implementation.
1061 * @param
1062 * @return
1063 */
1064STDMETHODIMP Appliance::COMGETTER(Disks)(ComSafeArrayOut(BSTR, aDisks))
1065{
1066 CheckComArgOutSafeArrayPointerValid(aDisks);
1067
1068 AutoCaller autoCaller(this);
1069 CheckComRCReturnRC(autoCaller.rc());
1070
1071 AutoReadLock alock(this);
1072
1073 size_t c = m->mapDisks.size();
1074 com::SafeArray<BSTR> sfaDisks(c);
1075
1076 DiskImagesMap::const_iterator it;
1077 size_t i = 0;
1078 for (it = m->mapDisks.begin();
1079 it != m->mapDisks.end();
1080 ++it, ++i)
1081 {
1082 // create a string representing this disk
1083 const DiskImage &d = it->second;
1084 char *psz = NULL;
1085 RTStrAPrintf(&psz,
1086 "%s\t"
1087 "%RI64\t"
1088 "%RI64\t"
1089 "%s\t"
1090 "%s\t"
1091 "%RI64\t"
1092 "%RI64\t"
1093 "%s",
1094 d.strDiskId.c_str(),
1095 d.iCapacity,
1096 d.iPopulatedSize,
1097 d.strFormat.c_str(),
1098 d.strHref.c_str(),
1099 d.iSize,
1100 d.iChunkSize,
1101 d.strCompression.c_str());
1102 Utf8Str utf(psz);
1103 Bstr bstr(utf);
1104 // push to safearray
1105 bstr.cloneTo(&sfaDisks[i]);
1106 RTStrFree(psz);
1107 }
1108
1109 sfaDisks.detachTo(ComSafeArrayOutArg(aDisks));
1110
1111 return S_OK;
1112}
1113
1114/**
1115 * Public method implementation.
1116 * @param
1117 * @return
1118 */
1119STDMETHODIMP Appliance::COMGETTER(VirtualSystemDescriptions)(ComSafeArrayOut(IVirtualSystemDescription*, aVirtualSystemDescriptions))
1120{
1121 CheckComArgOutSafeArrayPointerValid(aVirtualSystemDescriptions);
1122
1123 AutoCaller autoCaller(this);
1124 CheckComRCReturnRC(autoCaller.rc());
1125
1126 AutoReadLock alock(this);
1127
1128 SafeIfaceArray<IVirtualSystemDescription> sfaVSD(m->virtualSystemDescriptions);
1129 sfaVSD.detachTo(ComSafeArrayOutArg(aVirtualSystemDescriptions));
1130
1131 return S_OK;
1132}
1133
1134/**
1135 * Public method implementation.
1136 * @param path
1137 * @return
1138 */
1139STDMETHODIMP Appliance::Read(IN_BSTR path)
1140{
1141 HRESULT rc = S_OK;
1142
1143 if (!path)
1144 return E_POINTER;
1145
1146 AutoCaller autoCaller(this);
1147 CheckComRCReturnRC(autoCaller.rc());
1148
1149 AutoWriteLock alock(this);
1150
1151 // see if we can handle this file; for now we insist it has an ".ovf" extension
1152 m->strPath = path;
1153 if (!m->strPath.endsWith(".ovf", Utf8Str::CaseInsensitive))
1154 return setError(VBOX_E_FILE_ERROR,
1155 tr("Appliance file must have .ovf extension"));
1156
1157 try
1158 {
1159 xml::XmlFileParser parser;
1160 xml::Document doc;
1161 parser.read(m->strPath.raw(),
1162 doc);
1163
1164 const xml::ElementNode *pRootElem = doc.getRootElement();
1165 if (strcmp(pRootElem->getName(), "Envelope"))
1166 return setError(VBOX_E_FILE_ERROR,
1167 tr("Root element in OVF file must be \"Envelope\"."));
1168
1169 // OVF has the following rough layout:
1170 /*
1171 -- <References> .... files referenced from other parts of the file, such as VMDK images
1172 -- Metadata, comprised of several section commands
1173 -- virtual machines, either a single <VirtualSystem>, or a <VirtualSystemCollection>
1174 -- optionally <Strings> for localization
1175 */
1176
1177 // get all "File" child elements of "References" section so we can look up files easily;
1178 // first find the "References" sections so we can look up files
1179 xml::ElementNodesList listFileElements; // receives all /Envelope/References/File nodes
1180 const xml::ElementNode *pReferencesElem;
1181 if ((pReferencesElem = pRootElem->findChildElement("References")))
1182 pReferencesElem->getChildElements(listFileElements, "File");
1183
1184 // now go though the sections
1185 if (!(SUCCEEDED(rc = LoopThruSections(m->strPath.raw(), pReferencesElem, pRootElem))))
1186 return rc;
1187 }
1188 catch(xml::Error &x)
1189 {
1190 return setError(VBOX_E_FILE_ERROR,
1191 x.what());
1192 }
1193
1194 return S_OK;
1195}
1196
1197/**
1198 * Public method implementation.
1199 * @return
1200 */
1201STDMETHODIMP Appliance::Interpret()
1202{
1203 // @todo:
1204 // - don't use COM methods but the methods directly (faster, but needs appropriate locking of that objects itself (s. HardDisk))
1205 // - Appropriate handle errors like not supported file formats
1206 AutoCaller autoCaller(this);
1207 CheckComRCReturnRC(autoCaller.rc());
1208
1209 AutoWriteLock(this);
1210
1211 HRESULT rc = S_OK;
1212
1213 /* Clear any previous virtual system descriptions */
1214 m->virtualSystemDescriptions.clear();
1215
1216 /* We need the default path for storing disk images */
1217 ComPtr<ISystemProperties> systemProps;
1218 rc = mVirtualBox->COMGETTER(SystemProperties)(systemProps.asOutParam());
1219 CheckComRCReturnRC(rc);
1220 Bstr bstrDefaultHardDiskLocation;
1221 rc = systemProps->COMGETTER(DefaultHardDiskFolder)(bstrDefaultHardDiskLocation.asOutParam());
1222 CheckComRCReturnRC(rc);
1223
1224 /* Try/catch so we can clean up on error */
1225 try
1226 {
1227 list<VirtualSystem>::const_iterator it;
1228 /* Iterate through all virtual systems */
1229 for (it = m->llVirtualSystems.begin();
1230 it != m->llVirtualSystems.end();
1231 ++it)
1232 {
1233 const VirtualSystem &vsysThis = *it;
1234
1235 ComObjPtr<VirtualSystemDescription> pNewDesc;
1236 rc = pNewDesc.createObject();
1237 CheckComRCThrowRC(rc);
1238 rc = pNewDesc->init();
1239 CheckComRCThrowRC(rc);
1240
1241 /* Guest OS type */
1242 Utf8Str strOsTypeVBox,
1243 strCIMOSType = Utf8StrFmt("%RI32", (uint32_t)vsysThis.cimos);
1244 convertCIMOSType2VBoxOSType(strOsTypeVBox, vsysThis.cimos);
1245 pNewDesc->addEntry(VirtualSystemDescriptionType_OS,
1246 "",
1247 strCIMOSType,
1248 strOsTypeVBox);
1249
1250 /* VM name */
1251 /* If the there isn't any name specified create a default one out of
1252 * the OS type */
1253 Utf8Str nameVBox = vsysThis.strName;
1254 if (nameVBox.isEmpty())
1255 nameVBox = strOsTypeVBox;
1256 searchUniqueVMName(nameVBox);
1257 pNewDesc->addEntry(VirtualSystemDescriptionType_Name,
1258 "",
1259 vsysThis.strName,
1260 nameVBox);
1261
1262 /* VM description */
1263 if (!vsysThis.strDescription.isEmpty())
1264 pNewDesc->addEntry(VirtualSystemDescriptionType_Description,
1265 "",
1266 vsysThis.strDescription,
1267 vsysThis.strDescription);
1268
1269 /* VM license */
1270 if (!vsysThis.strLicenseText.isEmpty())
1271 pNewDesc->addEntry(VirtualSystemDescriptionType_License,
1272 "",
1273 vsysThis.strLicenseText,
1274 vsysThis.strLicenseText);
1275
1276 /* Now that we know the OS type, get our internal defaults based on that. */
1277 ComPtr<IGuestOSType> pGuestOSType;
1278 rc = mVirtualBox->GetGuestOSType(Bstr(strOsTypeVBox), pGuestOSType.asOutParam());
1279 CheckComRCThrowRC(rc);
1280
1281 /* CPU count */
1282 ULONG cpuCountVBox = vsysThis.cCPUs;
1283 /* Check for the constrains */
1284 if (cpuCountVBox > 1) //SchemaDefs::MaxCPUCount)
1285 {
1286 addWarning(tr("The virtual system \"%s\" claims support for %u CPU's, but VirtualBox has support for max %u CPU's only."),
1287 vsysThis.strName.c_str(), cpuCountVBox, 1); //SchemaDefs::MaxCPUCount);
1288 cpuCountVBox = 1; //SchemaDefs::MaxCPUCount;
1289 }
1290 if (vsysThis.cCPUs == 0)
1291 cpuCountVBox = 1;
1292 pNewDesc->addEntry(VirtualSystemDescriptionType_CPU,
1293 "",
1294 Utf8StrFmt("%RI32", (uint32_t)vsysThis.cCPUs),
1295 Utf8StrFmt("%RI32", (uint32_t)cpuCountVBox));
1296
1297 /* RAM */
1298 uint64_t ullMemSizeVBox = vsysThis.ullMemorySize / _1M;
1299 /* Check for the constrains */
1300 if (ullMemSizeVBox != 0 &&
1301 (ullMemSizeVBox < MM_RAM_MIN_IN_MB ||
1302 ullMemSizeVBox > MM_RAM_MAX_IN_MB))
1303 {
1304 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."),
1305 vsysThis.strName.c_str(), ullMemSizeVBox, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1306 ullMemSizeVBox = RT_MIN(RT_MAX(ullMemSizeVBox, MM_RAM_MIN_IN_MB), MM_RAM_MAX_IN_MB);
1307 }
1308 if (vsysThis.ullMemorySize == 0)
1309 {
1310 /* If the RAM of the OVF is zero, use our predefined values */
1311 ULONG memSizeVBox2;
1312 rc = pGuestOSType->COMGETTER(RecommendedRAM)(&memSizeVBox2);
1313 CheckComRCThrowRC(rc);
1314 /* VBox stores that in MByte */
1315 ullMemSizeVBox = (uint64_t)memSizeVBox2;
1316 }
1317 pNewDesc->addEntry(VirtualSystemDescriptionType_Memory,
1318 "",
1319 Utf8StrFmt("%RI64", (uint64_t)vsysThis.ullMemorySize),
1320 Utf8StrFmt("%RI64", (uint64_t)ullMemSizeVBox));
1321
1322 /* Audio */
1323 if (!vsysThis.strSoundCardType.isNull())
1324 /* Currently we set the AC97 always.
1325 @todo: figure out the hardware which could be possible */
1326 pNewDesc->addEntry(VirtualSystemDescriptionType_SoundCard,
1327 "",
1328 vsysThis.strSoundCardType,
1329 Utf8StrFmt("%RI32", (uint32_t)AudioControllerType_AC97));
1330
1331#ifdef VBOX_WITH_USB
1332 /* USB Controller */
1333 if (vsysThis.fHasUsbController)
1334 pNewDesc->addEntry(VirtualSystemDescriptionType_USBController, "", "", "");
1335#endif /* VBOX_WITH_USB */
1336
1337 /* Network Controller */
1338 uint32_t cEthernetAdapters = vsysThis.llEthernetAdapters.size();
1339 if (cEthernetAdapters > 0)
1340 {
1341 /* Check for the constrains */
1342 if (cEthernetAdapters > SchemaDefs::NetworkAdapterCount)
1343 addWarning(tr("The virtual system \"%s\" claims support for %u network adapters, but VirtualBox has support for max %u network adapter only."),
1344 vsysThis.strName.c_str(), cEthernetAdapters, SchemaDefs::NetworkAdapterCount);
1345
1346 /* Get the default network adapter type for the selected guest OS */
1347 NetworkAdapterType_T defaultAdapterVBox = NetworkAdapterType_Am79C970A;
1348 rc = pGuestOSType->COMGETTER(AdapterType)(&defaultAdapterVBox);
1349 CheckComRCThrowRC(rc);
1350
1351 EthernetAdaptersList::const_iterator itEA;
1352 /* Iterate through all abstract networks. We support 8 network
1353 * adapters at the maximum, so the first 8 will be added only. */
1354 size_t a = 0;
1355 for (itEA = vsysThis.llEthernetAdapters.begin();
1356 itEA != vsysThis.llEthernetAdapters.end() && a < SchemaDefs::NetworkAdapterCount;
1357 ++itEA, ++a)
1358 {
1359 const EthernetAdapter &ea = *itEA; // logical network to connect to
1360 Utf8Str strNetwork = ea.strNetworkName;
1361 // make sure it's one of these two
1362 if ( (strNetwork.compare("Null", Utf8Str::CaseInsensitive))
1363 && (strNetwork.compare("Bridged", Utf8Str::CaseInsensitive))
1364 && (strNetwork.compare("Internal", Utf8Str::CaseInsensitive))
1365 && (strNetwork.compare("HostOnly", Utf8Str::CaseInsensitive))
1366 )
1367 strNetwork = "NAT";
1368
1369 /* Figure out the hardware type */
1370 NetworkAdapterType_T nwAdapterVBox = defaultAdapterVBox;
1371 if (!ea.strAdapterType.compare("PCNet32", Utf8Str::CaseInsensitive))
1372 {
1373 /* If the default adapter is already one of the two
1374 * PCNet adapters use the default one. If not use the
1375 * Am79C970A as fallback. */
1376 if (!(defaultAdapterVBox == NetworkAdapterType_Am79C970A ||
1377 defaultAdapterVBox == NetworkAdapterType_Am79C973))
1378 nwAdapterVBox = NetworkAdapterType_Am79C970A;
1379 }
1380#ifdef VBOX_WITH_E1000
1381 else if (!ea.strAdapterType.compare("E1000", Utf8Str::CaseInsensitive))
1382 {
1383 /* If the default adapter is already one of the two
1384 * E1000 adapters use the default one. If not use the
1385 * I82540EM as fallback. */
1386 if (!(defaultAdapterVBox == NetworkAdapterType_I82540EM ||
1387 defaultAdapterVBox == NetworkAdapterType_I82543GC))
1388 nwAdapterVBox = NetworkAdapterType_I82540EM;
1389 }
1390#endif /* VBOX_WITH_E1000 */
1391
1392 pNewDesc->addEntry(VirtualSystemDescriptionType_NetworkAdapter,
1393 "", // ref
1394 strNetwork, // orig
1395 Utf8StrFmt("%RI32", (uint32_t)nwAdapterVBox), // conf
1396 Utf8StrFmt("type=%s", strNetwork.c_str())); // extra conf
1397 }
1398 }
1399
1400 /* Floppy Drive */
1401 if (vsysThis.fHasFloppyDrive)
1402 pNewDesc->addEntry(VirtualSystemDescriptionType_Floppy, "", "", "");
1403
1404 /* CD Drive */
1405 /* @todo: I can't disable the CDROM. So nothing to do for now */
1406 /*
1407 if (vsysThis.fHasCdromDrive)
1408 pNewDesc->addEntry(VirtualSystemDescriptionType_CDROM, "", "", "");*/
1409
1410 /* Hard disk Controller */
1411 uint16_t cIDEused = 0;
1412 uint16_t cSATAused = 0;
1413 uint16_t cSCSIused = 0;
1414 ControllersMap::const_iterator hdcIt;
1415 /* Iterate through all hard disk controllers */
1416 for (hdcIt = vsysThis.mapControllers.begin();
1417 hdcIt != vsysThis.mapControllers.end();
1418 ++hdcIt)
1419 {
1420 const HardDiskController &hdc = hdcIt->second;
1421 Utf8Str strControllerID = Utf8StrFmt("%RI32", (uint32_t)hdc.idController);
1422
1423 switch (hdc.system)
1424 {
1425 case HardDiskController::IDE:
1426 {
1427 /* Check for the constrains */
1428 /* @todo: I'm very confused! Are these bits *one* controller or
1429 is every port/bus declared as an extra controller. */
1430 if (cIDEused < 4)
1431 {
1432 // @todo: figure out the IDE types
1433 /* Use PIIX4 as default */
1434 Utf8Str strType = "PIIX4";
1435 if (!hdc.strControllerType.compare("PIIX3", Utf8Str::CaseInsensitive))
1436 strType = "PIIX3";
1437 else if (!hdc.strControllerType.compare("ICH6", Utf8Str::CaseInsensitive))
1438 strType = "ICH6";
1439 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerIDE,
1440 strControllerID,
1441 hdc.strControllerType,
1442 strType);
1443 }
1444 else
1445 {
1446 /* Warn only once */
1447 if (cIDEused == 1)
1448 addWarning(tr("The virtual \"%s\" system requests support for more than one IDE controller, but VirtualBox has support for only one."),
1449 vsysThis.strName.c_str());
1450
1451 }
1452 ++cIDEused;
1453 break;
1454 }
1455
1456#ifdef VBOX_WITH_AHCI
1457 case HardDiskController::SATA:
1458 {
1459 /* Check for the constrains */
1460 if (cSATAused < 1)
1461 {
1462 // @todo: figure out the SATA types
1463 /* We only support a plain AHCI controller, so use them always */
1464 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSATA,
1465 strControllerID,
1466 hdc.strControllerType,
1467 "AHCI");
1468 }
1469 else
1470 {
1471 /* Warn only once */
1472 if (cSATAused == 1)
1473 addWarning(tr("The virtual system \"%s\" requests support for more than one SATA controller, but VirtualBox has support for only one"),
1474 vsysThis.strName.c_str());
1475
1476 }
1477 ++cSATAused;
1478 break;
1479 }
1480#endif /* VBOX_WITH_AHCI */
1481
1482 case HardDiskController::SCSI:
1483 {
1484 /* Check for the constrains */
1485 if (cSCSIused < 1)
1486 {
1487 Utf8Str hdcController = "LsiLogic";
1488 if (!hdc.strControllerType.compare("BusLogic", Utf8Str::CaseInsensitive))
1489 hdcController = "BusLogic";
1490 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSCSI,
1491 strControllerID,
1492 hdc.strControllerType,
1493 hdcController);
1494 }
1495 else
1496 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."),
1497 vsysThis.strName.c_str(),
1498 hdc.strControllerType.c_str(),
1499 strControllerID.c_str());
1500 ++cSCSIused;
1501 break;
1502 }
1503 }
1504 }
1505
1506 /* Hard disks */
1507 if (vsysThis.mapVirtualDisks.size() > 0)
1508 {
1509 VirtualDisksMap::const_iterator itVD;
1510 /* Iterate through all hard disks ()*/
1511 for (itVD = vsysThis.mapVirtualDisks.begin();
1512 itVD != vsysThis.mapVirtualDisks.end();
1513 ++itVD)
1514 {
1515 const VirtualDisk &hd = itVD->second;
1516 /* Get the associated disk image */
1517 const DiskImage &di = m->mapDisks[hd.strDiskId];
1518
1519 // @todo:
1520 // - figure out all possible vmdk formats we also support
1521 // - figure out if there is a url specifier for vhd already
1522 // - we need a url specifier for the vdi format
1523 if ( di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#sparse", Utf8Str::CaseInsensitive)
1524 || di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#compressed", Utf8Str::CaseInsensitive))
1525 {
1526 /* If the href is empty use the VM name as filename */
1527 Utf8Str strFilename = di.strHref;
1528 if (!strFilename.length())
1529 strFilename = Utf8StrFmt("%s.vmdk", nameVBox.c_str());
1530 /* Construct a unique target path */
1531 Utf8StrFmt strPath("%ls%c%s",
1532 bstrDefaultHardDiskLocation.raw(),
1533 RTPATH_DELIMITER,
1534 strFilename.c_str());
1535 searchUniqueDiskImageFilePath(strPath);
1536
1537 /* find the description for the hard disk controller
1538 * that has the same ID as hd.idController */
1539 const VirtualSystemDescriptionEntry *pController;
1540 if (!(pController = pNewDesc->findControllerFromID(hd.idController)))
1541 throw setError(E_FAIL,
1542 tr("Cannot find hard disk controller with OVF instance ID %RI32 to which disk \"%s\" should be attached"),
1543 hd.idController,
1544 di.strHref.c_str());
1545
1546 /* controller to attach to, and the bus within that controller */
1547 Utf8StrFmt strExtraConfig("controller=%RI16;channel=%RI16",
1548 pController->ulIndex,
1549 hd.ulAddressOnParent);
1550 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskImage,
1551 hd.strDiskId,
1552 di.strHref,
1553 strPath,
1554 strExtraConfig);
1555 }
1556 else
1557 throw setError(VBOX_E_FILE_ERROR,
1558 tr("Unsupported format for virtual disk image in OVF: \"%s\"", di.strFormat.c_str()));
1559 }
1560 }
1561
1562 m->virtualSystemDescriptions.push_back(pNewDesc);
1563 }
1564 }
1565 catch (HRESULT aRC)
1566 {
1567 /* On error we clear the list & return */
1568 m->virtualSystemDescriptions.clear();
1569 rc = aRC;
1570 }
1571
1572 return rc;
1573}
1574
1575struct Appliance::TaskImportMachines
1576{
1577 TaskImportMachines(Appliance *aThat, Progress *aProgress)
1578 : pAppliance(aThat)
1579 , progress(aProgress)
1580 , rc(S_OK)
1581 {}
1582 ~TaskImportMachines() {}
1583
1584 HRESULT startThread();
1585
1586 Appliance *pAppliance;
1587 ComObjPtr<Progress> progress;
1588 HRESULT rc;
1589};
1590
1591HRESULT Appliance::TaskImportMachines::startThread()
1592{
1593 int vrc = RTThreadCreate(NULL, Appliance::taskThreadImportMachines, this,
1594 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
1595 "Appliance::Task");
1596 ComAssertMsgRCRet(vrc,
1597 ("Could not create taskThreadImportMachines (%Rrc)\n", vrc), E_FAIL);
1598
1599 return S_OK;
1600}
1601
1602/**
1603 * Public method implementation.
1604 * @param aProgress
1605 * @return
1606 */
1607STDMETHODIMP Appliance::ImportMachines(IProgress **aProgress)
1608{
1609 CheckComArgOutPointerValid(aProgress);
1610
1611 AutoCaller autoCaller(this);
1612 CheckComRCReturnRC(autoCaller.rc());
1613
1614 AutoReadLock(this);
1615
1616 HRESULT rc = S_OK;
1617
1618 ComObjPtr<Progress> progress;
1619 try
1620 {
1621 uint32_t opCount = calcMaxProgress();
1622 Bstr progressDesc = BstrFmt(tr("Import appliance '%s'"),
1623 m->strPath.raw());
1624 /* Create the progress object */
1625 progress.createObject();
1626 rc = progress->init(mVirtualBox, static_cast<IAppliance*>(this),
1627 progressDesc,
1628 FALSE /* aCancelable */,
1629 opCount,
1630 progressDesc);
1631 if (FAILED(rc)) throw rc;
1632
1633 /* Initialize our worker task */
1634 std::auto_ptr<TaskImportMachines> task(new TaskImportMachines(this, progress));
1635 //AssertComRCThrowRC (task->autoCaller.rc());
1636
1637 rc = task->startThread();
1638 if (FAILED(rc)) throw rc;
1639
1640 task.release();
1641 }
1642 catch (HRESULT aRC)
1643 {
1644 rc = aRC;
1645 }
1646
1647 if (SUCCEEDED(rc))
1648 /* Return progress to the caller */
1649 progress.queryInterfaceTo(aProgress);
1650
1651 return rc;
1652}
1653
1654struct MyHardDiskAttachment
1655{
1656 Guid uuid;
1657 ComPtr<IMachine> pMachine;
1658 Bstr controllerType;
1659 int32_t lChannel;
1660 int32_t lDevice;
1661};
1662
1663/**
1664 * Worker thread implementation for ImportMachines().
1665 * @param aThread
1666 * @param pvUser
1667 */
1668/* static */
1669DECLCALLBACK(int) Appliance::taskThreadImportMachines(RTTHREAD /* aThread */, void *pvUser)
1670{
1671 std::auto_ptr<TaskImportMachines> task(static_cast<TaskImportMachines*>(pvUser));
1672 AssertReturn(task.get(), VERR_GENERAL_FAILURE);
1673
1674 Appliance *pAppliance = task->pAppliance;
1675
1676 LogFlowFuncEnter();
1677 LogFlowFunc(("Appliance %p\n", pAppliance));
1678
1679 AutoCaller autoCaller(pAppliance);
1680 CheckComRCReturnRC(autoCaller.rc());
1681
1682 AutoWriteLock appLock(pAppliance);
1683
1684 HRESULT rc = S_OK;
1685
1686 ComPtr<IVirtualBox> pVirtualBox(pAppliance->mVirtualBox);
1687
1688 // rollback for errors:
1689 // 1) a list of images that we created/imported
1690 list<MyHardDiskAttachment> llHardDiskAttachments;
1691 list< ComPtr<IHardDisk> > llHardDisksCreated;
1692 list<Guid> llMachinesRegistered;
1693
1694 ComPtr<ISession> session;
1695 bool fSessionOpen = false;
1696 rc = session.createInprocObject(CLSID_Session);
1697 CheckComRCReturnRC(rc);
1698
1699 list<VirtualSystem>::const_iterator it;
1700 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it1;
1701 /* Iterate through all virtual systems of that appliance */
1702 size_t i = 0;
1703 for (it = pAppliance->m->llVirtualSystems.begin(),
1704 it1 = pAppliance->m->virtualSystemDescriptions.begin();
1705 it != pAppliance->m->llVirtualSystems.end();
1706 ++it, ++it1, ++i)
1707 {
1708 const VirtualSystem &vsysThis = *it;
1709 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it1);
1710
1711 ComPtr<IMachine> pNewMachine;
1712
1713 /* Catch possible errors */
1714 try
1715 {
1716 if (!task->progress.isNull())
1717 task->progress->advanceOperation(BstrFmt(tr("Importing Virtual System %d"), i + 1));
1718
1719 /* How many sub notifications are necessary? */
1720 const float opCountMax = 100.0/5;
1721 uint32_t opCount = 0;
1722
1723 /* Guest OS type */
1724 std::list<VirtualSystemDescriptionEntry*> vsdeOS;
1725 vsdeOS = vsdescThis->findByType(VirtualSystemDescriptionType_OS);
1726 if (vsdeOS.size() < 1)
1727 throw setError(VBOX_E_FILE_ERROR,
1728 tr("Missing guest OS type"));
1729 const Utf8Str &strOsTypeVBox = vsdeOS.front()->strVbox;
1730
1731 /* Now that we know the base system get our internal defaults based on that. */
1732 ComPtr<IGuestOSType> osType;
1733 rc = pVirtualBox->GetGuestOSType(Bstr(strOsTypeVBox), osType.asOutParam());
1734 if (FAILED(rc)) throw rc;
1735
1736 /* Create the machine */
1737 /* First get the name */
1738 std::list<VirtualSystemDescriptionEntry*> vsdeName = vsdescThis->findByType(VirtualSystemDescriptionType_Name);
1739 if (vsdeName.size() < 1)
1740 throw setError(VBOX_E_FILE_ERROR,
1741 tr("Missing VM name"));
1742 const Utf8Str &strNameVBox = vsdeName.front()->strVbox;
1743 rc = pVirtualBox->CreateMachine(Bstr(strNameVBox), Bstr(strOsTypeVBox),
1744 Bstr(), Guid(),
1745 pNewMachine.asOutParam());
1746 if (FAILED(rc)) throw rc;
1747
1748 // and the description
1749 std::list<VirtualSystemDescriptionEntry*> vsdeDescription = vsdescThis->findByType(VirtualSystemDescriptionType_Description);
1750 if (vsdeDescription.size())
1751 {
1752 const Utf8Str &strDescription = vsdeDescription.front()->strVbox;
1753 rc = pNewMachine->COMSETTER(Description)(Bstr(strDescription));
1754 if (FAILED(rc)) throw rc;
1755 }
1756
1757 if (!task->progress.isNull())
1758 rc = task->progress->notifyProgress((uint32_t)(opCountMax * opCount++));
1759
1760 /* CPU count (ignored for now) */
1761 // EntriesList vsdeCPU = vsd->findByType (VirtualSystemDescriptionType_CPU);
1762
1763 /* RAM */
1764 std::list<VirtualSystemDescriptionEntry*> vsdeRAM = vsdescThis->findByType(VirtualSystemDescriptionType_Memory);
1765 ComAssertMsgThrow(vsdeRAM.size() == 1, ("RAM size missing"), E_FAIL);
1766 const Utf8Str &memoryVBox = vsdeRAM.front()->strVbox;
1767 ULONG tt = (ULONG)RTStrToUInt64(memoryVBox.c_str());
1768 rc = pNewMachine->COMSETTER(MemorySize)(tt);
1769 if (FAILED(rc)) throw rc;
1770
1771 /* VRAM */
1772 /* Get the recommended VRAM for this guest OS type */
1773 ULONG vramVBox;
1774 rc = osType->COMGETTER(RecommendedVRAM)(&vramVBox);
1775 if (FAILED(rc)) throw rc;
1776
1777 /* Set the VRAM */
1778 rc = pNewMachine->COMSETTER(VRAMSize)(vramVBox);
1779 if (FAILED(rc)) throw rc;
1780
1781 if (!task->progress.isNull())
1782 task->progress->notifyProgress((uint32_t)(opCountMax * opCount++));
1783
1784 /* Audio Adapter */
1785 std::list<VirtualSystemDescriptionEntry*> vsdeAudioAdapter = vsdescThis->findByType(VirtualSystemDescriptionType_SoundCard);
1786 /* @todo: we support one audio adapter only */
1787 if (vsdeAudioAdapter.size() > 0)
1788 {
1789 const Utf8Str& audioAdapterVBox = vsdeAudioAdapter.front()->strVbox;
1790 if (audioAdapterVBox.compare("null", Utf8Str::CaseInsensitive) != 0)
1791 {
1792 uint32_t audio = RTStrToUInt32(audioAdapterVBox.c_str());
1793 ComPtr<IAudioAdapter> audioAdapter;
1794 rc = pNewMachine->COMGETTER(AudioAdapter)(audioAdapter.asOutParam());
1795 if (FAILED(rc)) throw rc;
1796 rc = audioAdapter->COMSETTER(Enabled)(true);
1797 if (FAILED(rc)) throw rc;
1798 rc = audioAdapter->COMSETTER(AudioController)(static_cast<AudioControllerType_T>(audio));
1799 if (FAILED(rc)) throw rc;
1800 }
1801 }
1802
1803#ifdef VBOX_WITH_USB
1804 /* USB Controller */
1805 std::list<VirtualSystemDescriptionEntry*> vsdeUSBController = vsdescThis->findByType(VirtualSystemDescriptionType_USBController);
1806 // USB support is enabled if there's at least one such entry; to disable USB support,
1807 // the type of the USB item would have been changed to "ignore"
1808 bool fUSBEnabled = vsdeUSBController.size() > 0;
1809
1810 ComPtr<IUSBController> usbController;
1811 rc = pNewMachine->COMGETTER(USBController)(usbController.asOutParam());
1812 if (FAILED(rc)) throw rc;
1813 rc = usbController->COMSETTER(Enabled)(fUSBEnabled);
1814 if (FAILED(rc)) throw rc;
1815#endif /* VBOX_WITH_USB */
1816
1817 if (!task->progress.isNull())
1818 task->progress->notifyProgress((uint32_t)(opCountMax * opCount++));
1819
1820 /* Change the network adapters */
1821 std::list<VirtualSystemDescriptionEntry*> vsdeNW = vsdescThis->findByType(VirtualSystemDescriptionType_NetworkAdapter);
1822 if (vsdeNW.size() == 0)
1823 {
1824 /* No network adapters, so we have to disable our default one */
1825 ComPtr<INetworkAdapter> nwVBox;
1826 rc = pNewMachine->GetNetworkAdapter(0, nwVBox.asOutParam());
1827 if (FAILED(rc)) throw rc;
1828 rc = nwVBox->COMSETTER(Enabled)(false);
1829 if (FAILED(rc)) throw rc;
1830 }
1831 else
1832 {
1833 list<VirtualSystemDescriptionEntry*>::const_iterator nwIt;
1834 /* Iterate through all network cards. We support 8 network adapters
1835 * at the maximum. (@todo: warn if there are more!) */
1836 size_t a = 0;
1837 for (nwIt = vsdeNW.begin();
1838 (nwIt != vsdeNW.end() && a < SchemaDefs::NetworkAdapterCount);
1839 ++nwIt, ++a)
1840 {
1841 const VirtualSystemDescriptionEntry* pvsys = *nwIt;
1842
1843 const Utf8Str &nwTypeVBox = pvsys->strVbox;
1844 uint32_t tt1 = RTStrToUInt32(nwTypeVBox.c_str());
1845 ComPtr<INetworkAdapter> pNetworkAdapter;
1846 rc = pNewMachine->GetNetworkAdapter((ULONG)a, pNetworkAdapter.asOutParam());
1847 if (FAILED(rc)) throw rc;
1848 /* Enable the network card & set the adapter type */
1849 rc = pNetworkAdapter->COMSETTER(Enabled)(true);
1850 if (FAILED(rc)) throw rc;
1851 rc = pNetworkAdapter->COMSETTER(AdapterType)(static_cast<NetworkAdapterType_T>(tt1));
1852 if (FAILED(rc)) throw rc;
1853
1854 // default is NAT; change to "bridged" if extra conf says so
1855 if (!pvsys->strExtraConfig.compare("type=Bridged", Utf8Str::CaseInsensitive))
1856 {
1857 /* Attach to the right interface */
1858 rc = pNetworkAdapter->AttachToBridgedInterface();
1859 if (FAILED(rc)) throw rc;
1860 ComPtr<IHost> host;
1861 rc = pVirtualBox->COMGETTER(Host)(host.asOutParam());
1862 if (FAILED(rc)) throw rc;
1863 com::SafeIfaceArray<IHostNetworkInterface> nwInterfaces;
1864 rc = host->COMGETTER(NetworkInterfaces)(ComSafeArrayAsOutParam(nwInterfaces));
1865 if (FAILED(rc)) throw rc;
1866 /* We search for the first host network interface which
1867 * is usable for bridged networking */
1868 for (size_t i=0; i < nwInterfaces.size(); ++i)
1869 {
1870 HostNetworkInterfaceType_T itype;
1871 rc = nwInterfaces[i]->COMGETTER(InterfaceType)(&itype);
1872 if (FAILED(rc)) throw rc;
1873 if (itype == HostNetworkInterfaceType_Bridged)
1874 {
1875 Bstr name;
1876 rc = nwInterfaces[i]->COMGETTER(Name)(name.asOutParam());
1877 if (FAILED(rc)) throw rc;
1878 /* Set the interface name to attach to */
1879 pNetworkAdapter->COMSETTER(HostInterface)(name);
1880 if (FAILED(rc)) throw rc;
1881 break;
1882 }
1883 }
1884 }
1885 /* Next test for host only interfaces */
1886 else if (!pvsys->strExtraConfig.compare("type=HostOnly", Utf8Str::CaseInsensitive))
1887 {
1888 /* Attach to the right interface */
1889 rc = pNetworkAdapter->AttachToHostOnlyInterface();
1890 if (FAILED(rc)) throw rc;
1891 ComPtr<IHost> host;
1892 rc = pVirtualBox->COMGETTER(Host)(host.asOutParam());
1893 if (FAILED(rc)) throw rc;
1894 com::SafeIfaceArray<IHostNetworkInterface> nwInterfaces;
1895 rc = host->COMGETTER(NetworkInterfaces)(ComSafeArrayAsOutParam(nwInterfaces));
1896 if (FAILED(rc)) throw rc;
1897 /* We search for the first host network interface which
1898 * is usable for host only networking */
1899 for (size_t i=0; i < nwInterfaces.size(); ++i)
1900 {
1901 HostNetworkInterfaceType_T itype;
1902 rc = nwInterfaces[i]->COMGETTER(InterfaceType)(&itype);
1903 if (FAILED(rc)) throw rc;
1904 if (itype == HostNetworkInterfaceType_HostOnly)
1905 {
1906 Bstr name;
1907 rc = nwInterfaces[i]->COMGETTER(Name)(name.asOutParam());
1908 if (FAILED(rc)) throw rc;
1909 /* Set the interface name to attach to */
1910 pNetworkAdapter->COMSETTER(HostInterface)(name);
1911 if (FAILED(rc)) throw rc;
1912 break;
1913 }
1914 }
1915 }
1916 }
1917 }
1918
1919 /* Floppy drive */
1920 std::list<VirtualSystemDescriptionEntry*> vsdeFloppy = vsdescThis->findByType(VirtualSystemDescriptionType_Floppy);
1921 // Floppy support is enabled if there's at least one such entry; to disable floppy support,
1922 // the type of the floppy item would have been changed to "ignore"
1923 bool fFloppyEnabled = vsdeFloppy.size() > 0;
1924 ComPtr<IFloppyDrive> floppyDrive;
1925 rc = pNewMachine->COMGETTER(FloppyDrive)(floppyDrive.asOutParam());
1926 if (FAILED(rc)) throw rc;
1927 rc = floppyDrive->COMSETTER(Enabled)(fFloppyEnabled);
1928 if (FAILED(rc)) throw rc;
1929
1930 if (!task->progress.isNull())
1931 task->progress->notifyProgress((uint32_t)(opCountMax * opCount++));
1932
1933 /* CDROM drive */
1934 /* @todo: I can't disable the CDROM. So nothing to do for now */
1935 // std::list<VirtualSystemDescriptionEntry*> vsdeFloppy = vsd->findByType(VirtualSystemDescriptionType_CDROM);
1936
1937 /* Hard disk controller IDE */
1938 std::list<VirtualSystemDescriptionEntry*> vsdeHDCIDE = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerIDE);
1939 if (vsdeHDCIDE.size() > 1)
1940 throw setError(VBOX_E_FILE_ERROR,
1941 tr("Too many IDE controllers in OVF; VirtualBox only supports one"));
1942 if (vsdeHDCIDE.size() == 1)
1943 {
1944 ComPtr<IStorageController> pController;
1945 rc = pNewMachine->GetStorageControllerByName(Bstr("IDE"), pController.asOutParam());
1946 if (FAILED(rc)) throw rc;
1947
1948 const char *pcszIDEType = vsdeHDCIDE.front()->strVbox.c_str();
1949 if (!strcmp(pcszIDEType, "PIIX3"))
1950 rc = pController->COMSETTER(ControllerType)(StorageControllerType_PIIX3);
1951 else if (!strcmp(pcszIDEType, "PIIX4"))
1952 rc = pController->COMSETTER(ControllerType)(StorageControllerType_PIIX4);
1953 else if (!strcmp(pcszIDEType, "ICH6"))
1954 rc = pController->COMSETTER(ControllerType)(StorageControllerType_ICH6);
1955 else
1956 throw setError(VBOX_E_FILE_ERROR,
1957 tr("Invalid IDE controller type \"%s\""),
1958 pcszIDEType);
1959 if (FAILED(rc)) throw rc;
1960 }
1961#ifdef VBOX_WITH_AHCI
1962 /* Hard disk controller SATA */
1963 std::list<VirtualSystemDescriptionEntry*> vsdeHDCSATA = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerSATA);
1964 if (vsdeHDCSATA.size() > 1)
1965 throw setError(VBOX_E_FILE_ERROR,
1966 tr("Too many SATA controllers in OVF; VirtualBox only supports one"));
1967 if (vsdeHDCSATA.size() > 0)
1968 {
1969 ComPtr<IStorageController> pController;
1970 const Utf8Str &hdcVBox = vsdeHDCSATA.front()->strVbox;
1971 if (hdcVBox == "AHCI")
1972 {
1973 rc = pNewMachine->AddStorageController(Bstr("SATA"), StorageBus_SATA, pController.asOutParam());
1974 if (FAILED(rc)) throw rc;
1975 }
1976 else
1977 throw setError(VBOX_E_FILE_ERROR,
1978 tr("Invalid SATA controller type \"%s\""),
1979 hdcVBox.c_str());
1980 }
1981#endif /* VBOX_WITH_AHCI */
1982
1983 /* Hard disk controller SCSI */
1984 std::list<VirtualSystemDescriptionEntry*> vsdeHDCSCSI = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerSCSI);
1985 if (vsdeHDCSCSI.size() > 1)
1986 throw setError(VBOX_E_FILE_ERROR,
1987 tr("Too many SCSI controllers in OVF; VirtualBox only supports one"));
1988 if (vsdeHDCSCSI.size() > 0)
1989 {
1990 ComPtr<IStorageController> pController;
1991 StorageControllerType_T controllerType;
1992 const Utf8Str &hdcVBox = vsdeHDCSCSI.front()->strVbox;
1993 if (hdcVBox == "LsiLogic")
1994 controllerType = StorageControllerType_LsiLogic;
1995 else if (hdcVBox == "BusLogic")
1996 controllerType = StorageControllerType_BusLogic;
1997 else
1998 throw setError(VBOX_E_FILE_ERROR,
1999 tr("Invalid SCSI controller type \"%s\""),
2000 hdcVBox.c_str());
2001
2002 rc = pNewMachine->AddStorageController(Bstr("SCSI"), StorageBus_SCSI, pController.asOutParam());
2003 if (FAILED(rc)) throw rc;
2004 rc = pController->COMSETTER(ControllerType)(controllerType);
2005 if (FAILED(rc)) throw rc;
2006 }
2007
2008 /* Now its time to register the machine before we add any hard disks */
2009 rc = pVirtualBox->RegisterMachine(pNewMachine);
2010 if (FAILED(rc)) throw rc;
2011
2012 Guid newMachineId;
2013 rc = pNewMachine->COMGETTER(Id)(newMachineId.asOutParam());
2014 if (FAILED(rc)) throw rc;
2015
2016 if (!task->progress.isNull())
2017 task->progress->notifyProgress((uint32_t)(opCountMax * opCount++));
2018
2019 // store new machine for roll-back in case of errors
2020 llMachinesRegistered.push_back(newMachineId);
2021
2022 /* Create the hard disks & connect them to the appropriate controllers. */
2023 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);
2024 if (avsdeHDs.size() > 0)
2025 {
2026 /* If in the next block an error occur we have to deregister
2027 the machine, so make an extra try/catch block. */
2028 ComPtr<IHardDisk> srcHdVBox;
2029 bool fSourceHdNeedsClosing = false;
2030
2031 try
2032 {
2033 /* In order to attach hard disks we need to open a session
2034 * for the new machine */
2035 rc = pVirtualBox->OpenSession(session, newMachineId);
2036 if (FAILED(rc)) throw rc;
2037 fSessionOpen = true;
2038
2039 /* The disk image has to be on the same place as the OVF file. So
2040 * strip the filename out of the full file path. */
2041 Utf8Str strSrcDir = stripFilename(pAppliance->m->strPath);
2042
2043 /* Iterate over all given disk images */
2044 list<VirtualSystemDescriptionEntry*>::const_iterator itHD;
2045 for (itHD = avsdeHDs.begin();
2046 itHD != avsdeHDs.end();
2047 ++itHD)
2048 {
2049 VirtualSystemDescriptionEntry *vsdeHD = *itHD;
2050
2051 const char *pcszDstFilePath = vsdeHD->strVbox.c_str();
2052 /* Check if the destination file exists already or the
2053 * destination path is empty. */
2054 if ( !(*pcszDstFilePath)
2055 || RTPathExists(pcszDstFilePath)
2056 )
2057 /* This isn't allowed */
2058 throw setError(VBOX_E_FILE_ERROR,
2059 tr("Destination file '%s' exists",
2060 pcszDstFilePath));
2061
2062 /* Find the disk from the OVF's disk list */
2063 DiskImagesMap::const_iterator itDiskImage = pAppliance->m->mapDisks.find(vsdeHD->strRef);
2064 /* vsdeHD->strRef contains the disk identifier (e.g. "vmdisk1"), which should exist
2065 in the virtual system's disks map under that ID and also in the global images map. */
2066 VirtualDisksMap::const_iterator itVirtualDisk = vsysThis.mapVirtualDisks.find(vsdeHD->strRef);
2067
2068 if ( itDiskImage == pAppliance->m->mapDisks.end()
2069 || itVirtualDisk == vsysThis.mapVirtualDisks.end()
2070 )
2071 throw setError(E_FAIL,
2072 tr("Internal inconsistency looking up disk images."));
2073
2074 const DiskImage &di = itDiskImage->second;
2075 const VirtualDisk &vd = itVirtualDisk->second;
2076
2077 /* Make sure all target directories exists */
2078 rc = VirtualBox::ensureFilePathExists(pcszDstFilePath);
2079 if (FAILED(rc))
2080 throw rc;
2081
2082 ComPtr<IProgress> progress;
2083
2084 ComPtr<IHardDisk> dstHdVBox;
2085 /* If strHref is empty we have to create a new file */
2086 if (di.strHref.isEmpty())
2087 {
2088 /* Which format to use? */
2089 Bstr srcFormat = L"VDI";
2090 if ( di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#sparse", Utf8Str::CaseInsensitive)
2091 || di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#compressed", Utf8Str::CaseInsensitive))
2092 srcFormat = L"VMDK";
2093 /* Create an empty hard disk */
2094 rc = pVirtualBox->CreateHardDisk(srcFormat, Bstr(pcszDstFilePath), dstHdVBox.asOutParam());
2095 if (FAILED(rc)) throw rc;
2096
2097 /* Create a dynamic growing disk image with the given capacity */
2098 rc = dstHdVBox->CreateBaseStorage(di.iCapacity / _1M, HardDiskVariant_Standard, progress.asOutParam());
2099 if (FAILED(rc)) throw rc;
2100
2101 /* Advance to the next operation */
2102 if (!task->progress.isNull())
2103 task->progress->advanceOperation (BstrFmt(tr("Creating virtual disk image '%s'"), pcszDstFilePath));
2104 }
2105 else
2106 {
2107 /* Construct the source file path */
2108 Utf8StrFmt strSrcFilePath("%s%c%s", strSrcDir.c_str(), RTPATH_DELIMITER, di.strHref.c_str());
2109 /* Check if the source file exists */
2110 if (!RTPathExists(strSrcFilePath.c_str()))
2111 /* This isn't allowed */
2112 throw setError(VBOX_E_FILE_ERROR,
2113 tr("Source virtual disk image file '%s' doesn't exist"),
2114 strSrcFilePath.c_str());
2115
2116 /* Clone the disk image (this is necessary cause the id has
2117 * to be recreated for the case the same hard disk is
2118 * attached already from a previous import) */
2119
2120 /* First open the existing disk image */
2121 rc = pVirtualBox->OpenHardDisk(Bstr(strSrcFilePath),
2122 AccessMode_ReadOnly,
2123 srcHdVBox.asOutParam());
2124 if (FAILED(rc)) throw rc;
2125 fSourceHdNeedsClosing = true;
2126
2127 /* We need the format description of the source disk image */
2128 Bstr srcFormat;
2129 rc = srcHdVBox->COMGETTER(Format)(srcFormat.asOutParam());
2130 if (FAILED(rc)) throw rc;
2131 /* Create a new hard disk interface for the destination disk image */
2132 rc = pVirtualBox->CreateHardDisk(srcFormat, Bstr(pcszDstFilePath), dstHdVBox.asOutParam());
2133 if (FAILED(rc)) throw rc;
2134 /* Clone the source disk image */
2135 rc = srcHdVBox->CloneTo(dstHdVBox, HardDiskVariant_Standard, progress.asOutParam());
2136 if (FAILED(rc)) throw rc;
2137
2138 /* Advance to the next operation */
2139 if (!task->progress.isNull())
2140 task->progress->advanceOperation (BstrFmt(tr("Importing virtual disk image '%s'"), strSrcFilePath.c_str()));
2141 }
2142
2143 // now loop until the asynchronous operation completes and then
2144 // report its result
2145 BOOL fCompleted;
2146 LONG currentPercent;
2147 while (SUCCEEDED(progress->COMGETTER(Completed(&fCompleted))))
2148 {
2149 rc = progress->COMGETTER(Percent(&currentPercent));
2150 if (FAILED(rc)) throw rc;
2151 if (!task->progress.isNull())
2152 task->progress->notifyProgress(currentPercent);
2153 if (fCompleted)
2154 break;
2155 /* Make sure the loop is not too tight */
2156 rc = progress->WaitForCompletion(100);
2157 if (FAILED(rc)) throw rc;
2158 }
2159 // report result of asynchronous operation
2160 HRESULT vrc;
2161 rc = progress->COMGETTER(ResultCode)(&vrc);
2162 if (FAILED(rc)) throw rc;
2163
2164 // if the thread of the progress object has an error, then
2165 // retrieve the error info from there, or it'll be lost
2166 if (FAILED(vrc))
2167 {
2168 ProgressErrorInfo info(progress);
2169 Utf8Str str(info.getText());
2170 const char *pcsz = str.c_str();
2171 HRESULT rc2 = setError(vrc,
2172 pcsz);
2173 throw rc2;
2174 }
2175
2176 if (fSourceHdNeedsClosing)
2177 {
2178 rc = srcHdVBox->Close();
2179 if (FAILED(rc)) throw rc;
2180 fSourceHdNeedsClosing = false;
2181 }
2182
2183 llHardDisksCreated.push_back(dstHdVBox);
2184 /* Now use the new uuid to attach the disk image to our new machine */
2185 ComPtr<IMachine> sMachine;
2186 rc = session->COMGETTER(Machine)(sMachine.asOutParam());
2187 if (FAILED(rc)) throw rc;
2188 Guid hdId;
2189 rc = dstHdVBox->COMGETTER(Id)(hdId.asOutParam());
2190 if (FAILED(rc)) throw rc;
2191
2192 /* For now we assume we have one controller of every type only */
2193 HardDiskController hdc = (*vsysThis.mapControllers.find(vd.idController)).second;
2194
2195 // this is for rollback later
2196 MyHardDiskAttachment mhda;
2197 mhda.uuid = newMachineId;
2198 mhda.pMachine = pNewMachine;
2199
2200 switch (hdc.system)
2201 {
2202 case HardDiskController::IDE:
2203 // For the IDE bus, the channel parameter can be either 0 or 1, to specify the primary
2204 // or secondary IDE controller, respectively. For the primary controller of the IDE bus,
2205 // the device number can be either 0 or 1, to specify the master or the slave device,
2206 // respectively. For the secondary IDE controller, the device number is always 1 because
2207 // the master device is reserved for the CD-ROM drive.
2208 mhda.controllerType = Bstr("IDE");
2209 switch (vd.ulAddressOnParent)
2210 {
2211 case 0: // interpret this as primary master
2212 mhda.lChannel = (long)0;
2213 mhda.lDevice = (long)0;
2214 break;
2215
2216 case 1: // interpret this as primary slave
2217 mhda.lChannel = (long)0;
2218 mhda.lDevice = (long)1;
2219 break;
2220
2221 case 2: // interpret this as secondary slave
2222 mhda.lChannel = (long)1;
2223 mhda.lDevice = (long)1;
2224 break;
2225
2226 default:
2227 throw setError(VBOX_E_NOT_SUPPORTED,
2228 tr("Invalid channel %RI16 specified; IDE controllers support only 0, 1 or 2"), vd.ulAddressOnParent);
2229 break;
2230 }
2231 break;
2232
2233 case HardDiskController::SATA:
2234 mhda.controllerType = Bstr("SATA");
2235 mhda.lChannel = (long)vd.ulAddressOnParent;
2236 mhda.lDevice = (long)0;
2237 break;
2238
2239 case HardDiskController::SCSI:
2240 mhda.controllerType = Bstr("SCSI");
2241 mhda.lChannel = (long)vd.ulAddressOnParent;
2242 mhda.lDevice = (long)0;
2243 break;
2244
2245 default: break;
2246 }
2247
2248 Log(("Attaching disk %s to channel %d on device %d\n", pcszDstFilePath, mhda.lChannel, mhda.lDevice));
2249
2250 rc = sMachine->AttachHardDisk(hdId,
2251 mhda.controllerType,
2252 mhda.lChannel,
2253 mhda.lDevice);
2254 if (FAILED(rc)) throw rc;
2255
2256 llHardDiskAttachments.push_back(mhda);
2257
2258 rc = sMachine->SaveSettings();
2259 if (FAILED(rc)) throw rc;
2260 } // end for (itHD = avsdeHDs.begin();
2261
2262 // only now that we're done with all disks, close the session
2263 rc = session->Close();
2264 if (FAILED(rc)) throw rc;
2265 fSessionOpen = false;
2266 }
2267 catch(HRESULT /* aRC */)
2268 {
2269 if (fSourceHdNeedsClosing)
2270 srcHdVBox->Close();
2271
2272 if (fSessionOpen)
2273 session->Close();
2274
2275 throw;
2276 }
2277 }
2278 }
2279 catch(HRESULT aRC)
2280 {
2281 rc = aRC;
2282 }
2283
2284 if (FAILED(rc))
2285 break;
2286
2287 } // for (it = pAppliance->m->llVirtualSystems.begin(),
2288
2289 if (FAILED(rc))
2290 {
2291 // with _whatever_ error we've had, do a complete roll-back of
2292 // machines and disks we've created; unfortunately this is
2293 // not so trivially done...
2294
2295 HRESULT rc2;
2296 // detach all hard disks from all machines we created
2297 list<MyHardDiskAttachment>::iterator itM;
2298 for (itM = llHardDiskAttachments.begin();
2299 itM != llHardDiskAttachments.end();
2300 ++itM)
2301 {
2302 const MyHardDiskAttachment &mhda = *itM;
2303 rc2 = pVirtualBox->OpenSession(session, mhda.uuid);
2304 if (SUCCEEDED(rc2))
2305 {
2306 ComPtr<IMachine> sMachine;
2307 rc2 = session->COMGETTER(Machine)(sMachine.asOutParam());
2308 if (SUCCEEDED(rc2))
2309 {
2310 rc2 = sMachine->DetachHardDisk(Bstr(mhda.controllerType), mhda.lChannel, mhda.lDevice);
2311 rc2 = sMachine->SaveSettings();
2312 }
2313 session->Close();
2314 }
2315 }
2316
2317 // now clean up all hard disks we created
2318 list< ComPtr<IHardDisk> >::iterator itHD;
2319 for (itHD = llHardDisksCreated.begin();
2320 itHD != llHardDisksCreated.end();
2321 ++itHD)
2322 {
2323 ComPtr<IHardDisk> pDisk = *itHD;
2324 ComPtr<IProgress> pProgress;
2325 rc2 = pDisk->DeleteStorage(pProgress.asOutParam());
2326 rc2 = pProgress->WaitForCompletion(-1);
2327 }
2328
2329 // finally, deregister and remove all machines
2330 list<Guid>::iterator itID;
2331 for (itID = llMachinesRegistered.begin();
2332 itID != llMachinesRegistered.end();
2333 ++itID)
2334 {
2335 const Guid &guid = *itID;
2336 ComPtr<IMachine> failedMachine;
2337 rc2 = pVirtualBox->UnregisterMachine(guid, failedMachine.asOutParam());
2338 if (SUCCEEDED(rc2))
2339 rc2 = failedMachine->DeleteSettings();
2340 }
2341 }
2342
2343 task->rc = rc;
2344
2345 if (!task->progress.isNull())
2346 task->progress->notifyComplete(rc);
2347
2348 LogFlowFunc(("rc=%Rhrc\n", rc));
2349 LogFlowFuncLeave();
2350
2351 return VINF_SUCCESS;
2352}
2353
2354struct Appliance::TaskWriteOVF
2355{
2356 TaskWriteOVF(Appliance *aThat, Progress *aProgress)
2357 : pAppliance(aThat)
2358 , progress(aProgress)
2359 , rc(S_OK)
2360 {}
2361 ~TaskWriteOVF() {}
2362
2363 HRESULT startThread();
2364
2365 Appliance *pAppliance;
2366 ComObjPtr<Progress> progress;
2367 HRESULT rc;
2368};
2369
2370HRESULT Appliance::TaskWriteOVF::startThread()
2371{
2372 int vrc = RTThreadCreate(NULL, Appliance::taskThreadWriteOVF, this,
2373 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
2374 "Appliance::Task");
2375 ComAssertMsgRCRet(vrc,
2376 ("Could not create taskThreadExportOVF (%Rrc)\n", vrc), E_FAIL);
2377
2378 return S_OK;
2379}
2380
2381STDMETHODIMP Appliance::Write(IN_BSTR path, IProgress **aProgress)
2382{
2383 HRESULT rc = S_OK;
2384
2385 CheckComArgOutPointerValid(aProgress);
2386
2387 AutoCaller autoCaller(this);
2388 if (FAILED(rc = autoCaller.rc())) return rc;
2389
2390 AutoWriteLock(this);
2391
2392 // see if we can handle this file; for now we insist it has an ".ovf" extension
2393 m->strPath = path;
2394 if (!m->strPath.endsWith(".ovf", Utf8Str::CaseInsensitive))
2395 return setError(VBOX_E_FILE_ERROR,
2396 tr("Appliance file must have .ovf extension"));
2397
2398 ComObjPtr<Progress> progress;
2399 try
2400 {
2401 uint32_t opCount = calcMaxProgress();
2402 Bstr progressDesc = BstrFmt(tr("Write appliance '%s'"),
2403 m->strPath.raw());
2404 /* Create the progress object */
2405 progress.createObject();
2406 rc = progress->init(mVirtualBox, static_cast<IAppliance*>(this),
2407 progressDesc,
2408 FALSE /* aCancelable */,
2409 opCount,
2410 progressDesc);
2411 CheckComRCThrowRC(rc);
2412
2413 /* Initialize our worker task */
2414 std::auto_ptr<TaskWriteOVF> task(new TaskWriteOVF(this, progress));
2415 //AssertComRCThrowRC (task->autoCaller.rc());
2416
2417 rc = task->startThread();
2418 CheckComRCThrowRC(rc);
2419
2420 task.release();
2421 }
2422 catch (HRESULT aRC)
2423 {
2424 rc = aRC;
2425 }
2426
2427 if (SUCCEEDED(rc))
2428 /* Return progress to the caller */
2429 progress.queryInterfaceTo(aProgress);
2430
2431 return rc;
2432}
2433
2434/**
2435 * Worker thread implementation for Write() (ovf writer).
2436 * @param aThread
2437 * @param pvUser
2438 */
2439/* static */
2440DECLCALLBACK(int) Appliance::taskThreadWriteOVF(RTTHREAD /* aThread */, void *pvUser)
2441{
2442 std::auto_ptr<TaskWriteOVF> task(static_cast<TaskWriteOVF*>(pvUser));
2443 AssertReturn(task.get(), VERR_GENERAL_FAILURE);
2444
2445 Appliance *pAppliance = task->pAppliance;
2446
2447 LogFlowFuncEnter();
2448 LogFlowFunc(("Appliance %p\n", pAppliance));
2449
2450 AutoCaller autoCaller(pAppliance);
2451 CheckComRCReturnRC(autoCaller.rc());
2452
2453 AutoWriteLock appLock(pAppliance);
2454
2455 HRESULT rc = S_OK;
2456
2457 ComPtr<IVirtualBox> pVirtualBox(pAppliance->mVirtualBox);
2458
2459 try
2460 {
2461 xml::Document doc;
2462 xml::ElementNode *pelmRoot = doc.createRootElement("Envelope");
2463
2464 pelmRoot->setAttribute("ovf:version", "1.0");
2465 pelmRoot->setAttribute("xml:lang", "en-US");
2466 pelmRoot->setAttribute("xmlns", "http://schemas.dmtf.org/ovf/envelope/1");
2467 pelmRoot->setAttribute("xmlns:ovf", "http://schemas.dmtf.org/ovf/envelope/1");
2468 pelmRoot->setAttribute("xmlns:ovfstr", "http://schema.dmtf.org/ovf/strings/1");
2469 pelmRoot->setAttribute("xmlns:rasd", "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData");
2470 pelmRoot->setAttribute("xmlns:vssd", "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData");
2471 pelmRoot->setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
2472 pelmRoot->setAttribute("xsi:schemaLocation", "http://schemas.dmtf.org/ovf/envelope/1 ../ovf-envelope.xsd");
2473
2474 // <Envelope>/<References>
2475 xml::ElementNode *pelmReferences = pelmRoot->createChild("References");
2476
2477 /* <Envelope>/<DiskSection>:
2478 <DiskSection>
2479 <Info>List of the virtual disks used in the package</Info>
2480 <Disk ovf:capacity="4294967296" ovf:diskId="lamp" ovf:format="http://www.vmware.com/specifications/vmdk.html#compressed" ovf:populatedSize="1924967692"/>
2481 </DiskSection> */
2482 xml::ElementNode *pelmDiskSection = pelmRoot->createChild("DiskSection");
2483 xml::ElementNode *pelmDiskSectionInfo = pelmDiskSection->createChild("Info");
2484 pelmDiskSectionInfo->addContent("List of the virtual disks used in the package");
2485 // for now, set up a map so we have a list of unique disk names (to make
2486 // sure the same disk name is only added once)
2487 map<Utf8Str, const VirtualSystemDescriptionEntry*> mapDisks;
2488
2489 /* <Envelope>/<NetworkSection>:
2490 <NetworkSection>
2491 <Info>Logical networks used in the package</Info>
2492 <Network ovf:name="VM Network">
2493 <Description>The network that the LAMP Service will be available on</Description>
2494 </Network>
2495 </NetworkSection> */
2496 xml::ElementNode *pelmNetworkSection = pelmRoot->createChild("NetworkSection");
2497 xml::ElementNode *pelmNetworkSectionInfo = pelmNetworkSection->createChild("Info");
2498 pelmNetworkSectionInfo->addContent("Logical networks used in the package");
2499 // for now, set up a map so we have a list of unique network names (to make
2500 // sure the same network name is only added once)
2501 map<Utf8Str, bool> mapNetworks;
2502 // we fill this later below when we iterate over the networks
2503
2504 // and here come the virtual systems:
2505 xml::ElementNode *pelmVirtualSystemCollection = pelmRoot->createChild("VirtualSystemCollection");
2506 /* xml::AttributeNode *pattrVirtualSystemCollectionId = */ pelmVirtualSystemCollection->setAttribute("ovf:id", "ExportedVirtualBoxMachines"); // whatever
2507
2508 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;
2509 /* Iterate through all virtual systems of that appliance */
2510 for (it = pAppliance->m->virtualSystemDescriptions.begin();
2511 it != pAppliance->m->virtualSystemDescriptions.end();
2512 ++it)
2513 {
2514 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);
2515
2516 xml::ElementNode *pelmVirtualSystem = pelmVirtualSystemCollection->createChild("VirtualSystem");
2517
2518 /*xml::ElementNode *pelmVirtualSystemInfo =*/ pelmVirtualSystem->createChild("Info")->addContent("A virtual machine");
2519
2520 std::list<VirtualSystemDescriptionEntry*> llName = vsdescThis->findByType(VirtualSystemDescriptionType_Name);
2521 if (llName.size() != 1)
2522 throw setError(VBOX_E_NOT_SUPPORTED,
2523 tr("Missing VM name"));
2524 pelmVirtualSystem->setAttribute("ovf:id", llName.front()->strVbox);
2525
2526 // description
2527 std::list<VirtualSystemDescriptionEntry*> llDescription = vsdescThis->findByType(VirtualSystemDescriptionType_Description);
2528 if (llDescription.size())
2529 {
2530 /* <Section ovf:required="false" xsi:type="ovf:AnnotationSection_Type">
2531 <Info>A human-readable annotation</Info>
2532 <Annotation>Plan 9</Annotation>
2533 </Section> */
2534 xml::ElementNode *pelmAnnotationSection = pelmVirtualSystem->createChild("AnnotationSection");
2535 pelmAnnotationSection->createChild("Info")->addContent("A human-readable annotation");
2536 pelmAnnotationSection->createChild("Annotation")->addContent(llDescription.front()->strVbox);
2537 }
2538
2539 // license
2540 std::list<VirtualSystemDescriptionEntry*> llLicense = vsdescThis->findByType(VirtualSystemDescriptionType_License);
2541 if (llLicense.size())
2542 {
2543 /* <EulaSection>
2544 <Info ovf:msgid="6">License agreement for the Virtual System.</Info>
2545 <License ovf:msgid="1">License terms can go in here.</License>
2546 </EulaSection> */
2547 xml::ElementNode *pelmAnnotationSection = pelmVirtualSystem->createChild("EulaSection");
2548 pelmAnnotationSection->createChild("Info")->addContent("License agreement for the Virtual System.");
2549 pelmAnnotationSection->createChild("License")->addContent(llLicense.front()->strVbox);
2550 }
2551
2552 // operating system
2553 std::list<VirtualSystemDescriptionEntry*> llOS = vsdescThis->findByType(VirtualSystemDescriptionType_OS);
2554 if (llOS.size() != 1)
2555 throw setError(VBOX_E_NOT_SUPPORTED,
2556 tr("Missing OS type"));
2557 /* <OperatingSystemSection ovf:id="82">
2558 <Info>Guest Operating System</Info>
2559 <Description>Linux 2.6.x</Description>
2560 </OperatingSystemSection> */
2561 xml::ElementNode *pelmOperatingSystemSection = pelmVirtualSystem->createChild("OperatingSystemSection");
2562 pelmOperatingSystemSection->setAttribute("ovf:id", llOS.front()->strOvf);
2563// pelmOperatingSystemSection->createChild("Info")->addContent("blah"); // @todo
2564// pelmOperatingSystemSection->createChild("Description")->addContent("blah"); // @todo
2565
2566 // <VirtualHardwareSection ovf:id="hw1" ovf:transport="iso">
2567 xml::ElementNode *pelmVirtualHardwareSection = pelmVirtualSystem->createChild("VirtualHardwareSection");
2568
2569 /* <System>
2570 <vssd:Description>Description of the virtual hardware section.</vssd:Description>
2571 <vssd:ElementName>vmware</vssd:ElementName>
2572 <vssd:InstanceID>1</vssd:InstanceID>
2573 <vssd:VirtualSystemIdentifier>MyLampService</vssd:VirtualSystemIdentifier>
2574 <vssd:VirtualSystemType>vmx-4</vssd:VirtualSystemType>
2575 </System> */
2576 xml::ElementNode *pelmSystem = pelmVirtualHardwareSection->createChild("System");
2577
2578 // <vssd:VirtualSystemType>vmx-4</vssd:VirtualSystemType>
2579 xml::ElementNode *pelmVirtualSystemType = pelmSystem->createChild("VirtualSystemType");
2580 pelmVirtualSystemType->addContent("virtualbox-2.2"); // instead of vmx-7?
2581
2582 // loop thru all description entries twice; once to write out all
2583 // devices _except_ disk images, and a second time to assign the
2584 // disk images; this is because disk images need to reference
2585 // IDE controllers, and we can't know their instance IDs without
2586 // assigning them first
2587
2588 uint32_t idIDEController = 0;
2589 int32_t lIDEControllerIndex = 0;
2590 uint32_t idSATAController = 0;
2591 int32_t lSATAControllerIndex = 0;
2592 uint32_t idSCSIController = 0;
2593 int32_t lSCSIControllerIndex = 0;
2594
2595 uint32_t ulInstanceID = 1;
2596 uint32_t cDisks = 0;
2597
2598 for (size_t uLoop = 1;
2599 uLoop <= 2;
2600 ++uLoop)
2601 {
2602 int32_t lIndexThis = 0;
2603 list<VirtualSystemDescriptionEntry>::const_iterator itD;
2604 for (itD = vsdescThis->m->llDescriptions.begin();
2605 itD != vsdescThis->m->llDescriptions.end();
2606 ++itD, ++lIndexThis)
2607 {
2608 const VirtualSystemDescriptionEntry &desc = *itD;
2609
2610 OVFResourceType_T type = (OVFResourceType_T)0; // if this becomes != 0 then we do stuff
2611 Utf8Str strResourceSubType;
2612
2613 Utf8Str strDescription; // results in <rasd:Description>...</rasd:Description> block
2614 Utf8Str strCaption; // results in <rasd:Caption>...</rasd:Caption> block
2615
2616 uint32_t ulParent = 0;
2617
2618 int32_t lVirtualQuantity = -1;
2619 Utf8Str strAllocationUnits;
2620
2621 int32_t lAddress = -1;
2622 int32_t lBusNumber = -1;
2623 int32_t lAddressOnParent = -1;
2624
2625 int32_t lAutomaticAllocation = -1; // 0 means "false", 1 means "true"
2626 Utf8Str strConnection; // results in <rasd:Connection>...</rasd:Connection> block
2627 Utf8Str strHostResource;
2628
2629 uint64_t uTemp;
2630
2631 switch (desc.type)
2632 {
2633 case VirtualSystemDescriptionType_CPU:
2634 /* <Item>
2635 <rasd:Caption>1 virtual CPU</rasd:Caption>
2636 <rasd:Description>Number of virtual CPUs</rasd:Description>
2637 <rasd:ElementName>virtual CPU</rasd:ElementName>
2638 <rasd:InstanceID>1</rasd:InstanceID>
2639 <rasd:ResourceType>3</rasd:ResourceType>
2640 <rasd:VirtualQuantity>1</rasd:VirtualQuantity>
2641 </Item> */
2642 if (uLoop == 1)
2643 {
2644 strDescription = "Number of virtual CPUs";
2645 type = OVFResourceType_Processor; // 3
2646 lVirtualQuantity = 1;
2647 }
2648 break;
2649
2650 case VirtualSystemDescriptionType_Memory:
2651 /* <Item>
2652 <rasd:AllocationUnits>MegaBytes</rasd:AllocationUnits>
2653 <rasd:Caption>256 MB of memory</rasd:Caption>
2654 <rasd:Description>Memory Size</rasd:Description>
2655 <rasd:ElementName>Memory</rasd:ElementName>
2656 <rasd:InstanceID>2</rasd:InstanceID>
2657 <rasd:ResourceType>4</rasd:ResourceType>
2658 <rasd:VirtualQuantity>256</rasd:VirtualQuantity>
2659 </Item> */
2660 if (uLoop == 1)
2661 {
2662 strDescription = "Memory Size";
2663 type = OVFResourceType_Memory; // 4
2664 desc.strVbox.toInt(uTemp);
2665 lVirtualQuantity = (int32_t)(uTemp / _1M);
2666 strAllocationUnits = "MegaBytes";
2667 }
2668 break;
2669
2670 case VirtualSystemDescriptionType_HardDiskControllerIDE:
2671 /* <Item>
2672 <rasd:Caption>ideController1</rasd:Caption>
2673 <rasd:Description>IDE Controller</rasd:Description>
2674 <rasd:InstanceId>5</rasd:InstanceId>
2675 <rasd:ResourceType>5</rasd:ResourceType>
2676 <rasd:Address>1</rasd:Address>
2677 <rasd:BusNumber>1</rasd:BusNumber>
2678 </Item> */
2679 if (uLoop == 1)
2680 {
2681 strDescription = "IDE Controller";
2682 type = OVFResourceType_IDEController; // 5
2683 // it seems that OVFTool always writes these two, and since we can only
2684 // have one IDE controller, we'll use this as well
2685 lAddress = 1;
2686 lBusNumber = 1;
2687
2688 // remember this ID
2689 idIDEController = ulInstanceID;
2690 lIDEControllerIndex = lIndexThis;
2691 }
2692 break;
2693
2694 case VirtualSystemDescriptionType_HardDiskControllerSATA:
2695 /* <Item>
2696 <rasd:Caption>sataController0</rasd:Caption>
2697 <rasd:Description>SATA Controller</rasd:Description>
2698 <rasd:InstanceId>4</rasd:InstanceId>
2699 <rasd:ResourceType>20</rasd:ResourceType>
2700 <rasd:ResourceSubType>ahci</rasd:ResourceSubType>
2701 <rasd:Address>0</rasd:Address>
2702 <rasd:BusNumber>0</rasd:BusNumber>
2703 </Item>
2704 */
2705 if (uLoop == 1)
2706 {
2707 strDescription = "SATA Controller";
2708 strCaption = "sataController0";
2709 type = OVFResourceType_OtherStorageDevice; // 20
2710 // it seems that OVFTool always writes these two, and since we can only
2711 // have one SATA controller, we'll use this as well
2712 lAddress = 0;
2713 lBusNumber = 0;
2714
2715 if ( desc.strVbox.isEmpty() // AHCI is the default in VirtualBox
2716 || (!desc.strVbox.compare("ahci", Utf8Str::CaseInsensitive))
2717 )
2718 strResourceSubType = "AHCI";
2719 else
2720 throw setError(VBOX_E_NOT_SUPPORTED,
2721 tr("Invalid config string \"%s\" in SATA controller"), desc.strVbox.c_str());
2722
2723 // remember this ID
2724 idSATAController = ulInstanceID;
2725 lSATAControllerIndex = lIndexThis;
2726 }
2727 break;
2728
2729 case VirtualSystemDescriptionType_HardDiskControllerSCSI:
2730 /* <Item>
2731 <rasd:Caption>scsiController0</rasd:Caption>
2732 <rasd:Description>SCSI Controller</rasd:Description>
2733 <rasd:InstanceId>4</rasd:InstanceId>
2734 <rasd:ResourceType>6</rasd:ResourceType>
2735 <rasd:ResourceSubType>buslogic</rasd:ResourceSubType>
2736 <rasd:Address>0</rasd:Address>
2737 <rasd:BusNumber>0</rasd:BusNumber>
2738 </Item>
2739 */
2740 if (uLoop == 1)
2741 {
2742 strDescription = "SCSI Controller";
2743 strCaption = "scsiController0";
2744 type = OVFResourceType_ParallelSCSIHBA; // 6
2745 // it seems that OVFTool always writes these two, and since we can only
2746 // have one SATA controller, we'll use this as well
2747 lAddress = 0;
2748 lBusNumber = 0;
2749
2750 if ( desc.strVbox.isEmpty() // LsiLogic is the default in VirtualBox
2751 || (!desc.strVbox.compare("lsilogic", Utf8Str::CaseInsensitive))
2752 )
2753 strResourceSubType = "lsilogic";
2754 else if (!desc.strVbox.compare("buslogic", Utf8Str::CaseInsensitive))
2755 strResourceSubType = "buslogic";
2756 else
2757 throw setError(VBOX_E_NOT_SUPPORTED,
2758 tr("Invalid config string \"%s\" in SCSI controller"), desc.strVbox.c_str());
2759
2760 // remember this ID
2761 idSCSIController = ulInstanceID;
2762 lSCSIControllerIndex = lIndexThis;
2763 }
2764 break;
2765
2766 case VirtualSystemDescriptionType_HardDiskImage:
2767 /* <Item>
2768 <rasd:Caption>disk1</rasd:Caption>
2769 <rasd:InstanceId>8</rasd:InstanceId>
2770 <rasd:ResourceType>17</rasd:ResourceType>
2771 <rasd:HostResource>/disk/vmdisk1</rasd:HostResource>
2772 <rasd:Parent>4</rasd:Parent>
2773 <rasd:AddressOnParent>0</rasd:AddressOnParent>
2774 </Item> */
2775 if (uLoop == 2)
2776 {
2777 Utf8Str strDiskID = Utf8StrFmt("vmdisk%RI32", ++cDisks);
2778
2779 strDescription = "Disk Image";
2780 strCaption = Utf8StrFmt("disk%RI32", cDisks); // this is not used for anything else
2781 type = OVFResourceType_HardDisk; // 17
2782
2783 // the following references the "<Disks>" XML block
2784 strHostResource = Utf8StrFmt("/disk/%s", strDiskID.c_str());
2785
2786 // controller=<index>;channel=<c>
2787 size_t pos1 = desc.strExtraConfig.find("controller=");
2788 size_t pos2 = desc.strExtraConfig.find("channel=");
2789 if (pos1 != Utf8Str::npos)
2790 {
2791 int32_t lControllerIndex = -1;
2792 RTStrToInt32Ex(desc.strExtraConfig.c_str() + pos1 + 11, NULL, 0, &lControllerIndex);
2793 if (lControllerIndex == lIDEControllerIndex)
2794 ulParent = idIDEController;
2795 else if (lControllerIndex == lSCSIControllerIndex)
2796 ulParent = idSCSIController;
2797 else if (lControllerIndex == lSATAControllerIndex)
2798 ulParent = idSATAController;
2799 }
2800 if (pos2 != Utf8Str::npos)
2801 RTStrToInt32Ex(desc.strExtraConfig.c_str() + pos2 + 8, NULL, 0, &lAddressOnParent);
2802
2803 if ( !ulParent
2804 || lAddressOnParent == -1
2805 )
2806 throw setError(VBOX_E_NOT_SUPPORTED,
2807 tr("Missing or bad extra config string in hard disk image: \"%s\""), desc.strExtraConfig.c_str());
2808
2809 mapDisks[strDiskID] = &desc;
2810 }
2811 break;
2812
2813 case VirtualSystemDescriptionType_Floppy:
2814 if (uLoop == 1)
2815 {
2816 strDescription = "Floppy Drive";
2817 strCaption = "floppy0"; // this is what OVFTool writes
2818 type = OVFResourceType_FloppyDrive; // 14
2819 lAutomaticAllocation = 0;
2820 lAddressOnParent = 0; // this is what OVFTool writes
2821 }
2822 break;
2823
2824 case VirtualSystemDescriptionType_CDROM:
2825 if (uLoop == 2)
2826 {
2827 // we can't have a CD without an IDE controller
2828 if (!idIDEController)
2829 throw setError(VBOX_E_NOT_SUPPORTED,
2830 tr("Can't have CD-ROM without IDE controller"));
2831
2832 strDescription = "CD-ROM Drive";
2833 strCaption = "cdrom1"; // this is what OVFTool writes
2834 type = OVFResourceType_CDDrive; // 15
2835 lAutomaticAllocation = 1;
2836 ulParent = idIDEController;
2837 lAddressOnParent = 0; // this is what OVFTool writes
2838 }
2839 break;
2840
2841 case VirtualSystemDescriptionType_NetworkAdapter:
2842 /* <Item>
2843 <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
2844 <rasd:Caption>Ethernet adapter on 'VM Network'</rasd:Caption>
2845 <rasd:Connection>VM Network</rasd:Connection>
2846 <rasd:ElementName>VM network</rasd:ElementName>
2847 <rasd:InstanceID>3</rasd:InstanceID>
2848 <rasd:ResourceType>10</rasd:ResourceType>
2849 </Item> */
2850 if (uLoop == 1)
2851 {
2852 lAutomaticAllocation = 1;
2853 strCaption = Utf8StrFmt("Ethernet adapter on '%s'", desc.strOvf.c_str());
2854 type = OVFResourceType_EthernetAdapter; // 10
2855 /* Set the hardware type to something useful.
2856 * To be compatible with vmware & others we set
2857 * PCNet32 for our PCNet types & E1000 for the
2858 * E1000 cards. */
2859 switch (desc.strVbox.toInt32())
2860 {
2861 case NetworkAdapterType_Am79C970A:
2862 case NetworkAdapterType_Am79C973: strResourceSubType = "PCNet32"; break;
2863#ifdef VBOX_WITH_E1000
2864 case NetworkAdapterType_I82540EM:
2865 case NetworkAdapterType_I82543GC: strResourceSubType = "E1000"; break;
2866#endif /* VBOX_WITH_E1000 */
2867 }
2868 strConnection = desc.strOvf;
2869
2870 mapNetworks[desc.strOvf] = true;
2871 }
2872 break;
2873
2874 case VirtualSystemDescriptionType_USBController:
2875 /* <Item ovf:required="false">
2876 <rasd:Caption>usb</rasd:Caption>
2877 <rasd:Description>USB Controller</rasd:Description>
2878 <rasd:InstanceId>3</rasd:InstanceId>
2879 <rasd:ResourceType>23</rasd:ResourceType>
2880 <rasd:Address>0</rasd:Address>
2881 <rasd:BusNumber>0</rasd:BusNumber>
2882 </Item> */
2883 if (uLoop == 1)
2884 {
2885 strDescription = "USB Controller";
2886 strCaption = "usb";
2887 type = OVFResourceType_USBController; // 23
2888 lAddress = 0; // this is what OVFTool writes
2889 lBusNumber = 0; // this is what OVFTool writes
2890 }
2891 break;
2892
2893 case VirtualSystemDescriptionType_SoundCard:
2894 /* <Item ovf:required="false">
2895 <rasd:Caption>sound</rasd:Caption>
2896 <rasd:Description>Sound Card</rasd:Description>
2897 <rasd:InstanceId>10</rasd:InstanceId>
2898 <rasd:ResourceType>35</rasd:ResourceType>
2899 <rasd:ResourceSubType>ensoniq1371</rasd:ResourceSubType>
2900 <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
2901 <rasd:AddressOnParent>3</rasd:AddressOnParent>
2902 </Item> */
2903 if (uLoop == 1)
2904 {
2905 strDescription = "Sound Card";
2906 strCaption = "sound";
2907 type = OVFResourceType_SoundCard; // 35
2908 strResourceSubType = desc.strOvf; // e.g. ensoniq1371
2909 lAutomaticAllocation = 0;
2910 lAddressOnParent = 3; // what gives? this is what OVFTool writes
2911 }
2912 break;
2913 }
2914
2915 if (type)
2916 {
2917 xml::ElementNode *pItem;
2918
2919 pItem = pelmVirtualHardwareSection->createChild("Item");
2920
2921 if (!strDescription.isEmpty())
2922 pItem->createChild("rasd:Description")->addContent(strDescription);
2923 if (!strCaption.isEmpty())
2924 pItem->createChild("rasd:Caption")->addContent(strCaption);
2925
2926 if (!strAllocationUnits.isEmpty())
2927 pItem->createChild("rasd:AllocationUnits")->addContent(strAllocationUnits);
2928
2929 if (lAutomaticAllocation != -1)
2930 pItem->createChild("rasd:AutomaticAllocation")->addContent( (lAutomaticAllocation) ? "true" : "false" );
2931
2932 if (!strConnection.isEmpty())
2933 pItem->createChild("rasd:Connection")->addContent(strConnection);
2934
2935 // <rasd:InstanceID>1</rasd:InstanceID>
2936 pItem->createChild("rasd:InstanceID")->addContent(Utf8StrFmt("%d", ulInstanceID));
2937 ++ulInstanceID;
2938
2939 // <rasd:ResourceType>3</rasd:ResourceType>
2940 pItem->createChild("rasd:ResourceType")->addContent(Utf8StrFmt("%d", type));
2941 if (!strResourceSubType.isEmpty())
2942 pItem->createChild("rasd:ResourceSubType")->addContent(strResourceSubType);
2943
2944 // <rasd:VirtualQuantity>1</rasd:VirtualQuantity>
2945 if (lVirtualQuantity != -1)
2946 pItem->createChild("rasd:VirtualQuantity")->addContent(Utf8StrFmt("%d", lVirtualQuantity));
2947
2948 if (lAddress != -1)
2949 pItem->createChild("rasd:Address")->addContent(Utf8StrFmt("%d", lAddress));
2950
2951 if (lBusNumber != -1)
2952 pItem->createChild("rasd:BusNumber")->addContent(Utf8StrFmt("%d", lBusNumber));
2953
2954 if (ulParent)
2955 pItem->createChild("rasd:Parent")->addContent(Utf8StrFmt("%d", ulParent));
2956 if (lAddressOnParent != -1)
2957 pItem->createChild("rasd:AddressOnParent")->addContent(Utf8StrFmt("%d", lAddressOnParent));
2958
2959 if (!strHostResource.isEmpty())
2960 pItem->createChild("rasd:HostResource")->addContent(strHostResource);
2961 }
2962 }
2963 } // for (size_t uLoop = 0; ...
2964 }
2965
2966 // finally, fill in the network section we set up empty above according
2967 // to the networks we found with the hardware items
2968 map<Utf8Str, bool>::const_iterator itN;
2969 for (itN = mapNetworks.begin();
2970 itN != mapNetworks.end();
2971 ++itN)
2972 {
2973 const Utf8Str &strNetwork = itN->first;
2974 xml::ElementNode *pelmNetwork = pelmNetworkSection->createChild("Network");
2975 pelmNetwork->setAttribute("ovf:name", strNetwork.c_str());
2976 pelmNetwork->createChild("Description")->addContent("Logical network used by this appliance.");
2977 }
2978
2979 map<Utf8Str, const VirtualSystemDescriptionEntry*>::const_iterator itS;
2980 uint32_t ulFile = 1;
2981 for (itS = mapDisks.begin();
2982 itS != mapDisks.end();
2983 ++itS)
2984 {
2985 const Utf8Str &strDiskID = itS->first;
2986 const VirtualSystemDescriptionEntry *pDiskEntry = itS->second;
2987
2988 // source path: where the VBox image is
2989 const Utf8Str &strSrcFilePath = pDiskEntry->strVbox;
2990 Bstr bstrSrcFilePath(strSrcFilePath);
2991 if (!RTPathExists(strSrcFilePath.c_str()))
2992 /* This isn't allowed */
2993 throw setError(VBOX_E_FILE_ERROR,
2994 tr("Source virtual disk image file '%s' doesn't exist"),
2995 strSrcFilePath.c_str());
2996
2997 // output filename
2998 const Utf8Str &strTargetFileNameOnly = pDiskEntry->strOvf;
2999 // target path needs to be composed from where the output OVF is
3000 Utf8Str strTargetFilePath = stripFilename(pAppliance->m->strPath);
3001 strTargetFilePath.append("/");
3002 strTargetFilePath.append(strTargetFileNameOnly);
3003
3004 // clone the disk:
3005 ComPtr<IHardDisk> pSourceDisk;
3006 ComPtr<IHardDisk> pTargetDisk;
3007 ComPtr<IProgress> pProgress2;
3008
3009 Log(("Finding source disk \"%ls\"\n", bstrSrcFilePath.raw()));
3010 rc = pVirtualBox->FindHardDisk(bstrSrcFilePath, pSourceDisk.asOutParam());
3011 if (FAILED(rc)) throw rc;
3012
3013 /* We are always exporting to vmdfk stream optimized for now */
3014 Bstr bstrSrcFormat = L"VMDK";
3015
3016 // create a new hard disk interface for the destination disk image
3017 Log(("Creating target disk \"%s\"\n", strTargetFilePath.raw()));
3018 rc = pVirtualBox->CreateHardDisk(bstrSrcFormat, Bstr(strTargetFilePath), pTargetDisk.asOutParam());
3019 if (FAILED(rc)) throw rc;
3020
3021 // the target disk is now registered and needs to be removed again,
3022 // both after successful cloning or if anything goes bad!
3023 try
3024 {
3025 // create a flat copy of the source disk image
3026 rc = pSourceDisk->FlattenTo(pTargetDisk, HardDiskVariant_VmdkStreamOptimized, pProgress2.asOutParam());
3027 if (FAILED(rc)) throw rc;
3028
3029 // advance to the next operation
3030 if (!task->progress.isNull())
3031 task->progress->advanceOperation(BstrFmt(tr("Exporting virtual disk image '%s'"), strSrcFilePath.c_str()));
3032
3033 // now loop until the asynchronous operation completes and then
3034 // report its result
3035 BOOL fCompleted;
3036 LONG currentPercent;
3037 while (SUCCEEDED(pProgress2->COMGETTER(Completed(&fCompleted))))
3038 {
3039 rc = pProgress2->COMGETTER(Percent(&currentPercent));
3040 if (FAILED(rc)) throw rc;
3041 if (!task->progress.isNull())
3042 task->progress->notifyProgress(currentPercent);
3043 if (fCompleted)
3044 break;
3045 // make sure the loop is not too tight
3046 rc = pProgress2->WaitForCompletion(100);
3047 if (FAILED(rc)) throw rc;
3048 }
3049
3050 // report result of asynchronous operation
3051 HRESULT vrc;
3052 rc = pProgress2->COMGETTER(ResultCode)(&vrc);
3053 if (FAILED(rc)) throw rc;
3054
3055 // if the thread of the progress object has an error, then
3056 // retrieve the error info from there, or it'll be lost
3057 if (FAILED(vrc))
3058 {
3059 ProgressErrorInfo info(pProgress2);
3060 Utf8Str str(info.getText());
3061 const char *pcsz = str.c_str();
3062 HRESULT rc2 = setError(vrc, pcsz);
3063 throw rc2;
3064 }
3065 }
3066 catch (HRESULT rc3)
3067 {
3068 // upon error after registereing, close the disk or
3069 // it'll stick in the registry forever
3070 pTargetDisk->Close();
3071 throw rc3;
3072 }
3073
3074 // we need the following for the XML
3075 uint64_t cbFile = 0; // actual file size
3076 rc = pTargetDisk->COMGETTER(Size)(&cbFile);
3077 if (FAILED(rc)) throw rc;
3078
3079 ULONG64 cbCapacity = 0; // size reported to guest
3080 rc = pTargetDisk->COMGETTER(LogicalSize)(&cbCapacity);
3081 if (FAILED(rc)) throw rc;
3082 // capacity is reported in megabytes, so...
3083 cbCapacity *= _1M;
3084
3085 // upon success, close the disk as well
3086 rc = pTargetDisk->Close();
3087 if (FAILED(rc)) throw rc;
3088
3089 // now handle the XML for the disk:
3090 Utf8StrFmt strFileRef("file%RI32", ulFile++);
3091 // <File ovf:href="WindowsXpProfessional-disk1.vmdk" ovf:id="file1" ovf:size="1710381056"/>
3092 xml::ElementNode *pelmFile = pelmReferences->createChild("File");
3093 pelmFile->setAttribute("ovf:href", strTargetFileNameOnly);
3094 pelmFile->setAttribute("ovf:id", strFileRef);
3095 pelmFile->setAttribute("ovf:size", Utf8StrFmt("%RI64", cbFile).c_str());
3096
3097 // add disk to XML Disks section
3098 // <Disk ovf:capacity="8589934592" ovf:diskId="vmdisk1" ovf:fileRef="file1" ovf:format="http://www.vmware.com/specifications/vmdk.html#sparse"/>
3099 xml::ElementNode *pelmDisk = pelmDiskSection->createChild("Disk");
3100 pelmDisk->setAttribute("ovf:capacity", Utf8StrFmt("%RI64", cbCapacity).c_str());
3101 pelmDisk->setAttribute("ovf:diskId", strDiskID);
3102 pelmDisk->setAttribute("ovf:fileRef", strFileRef);
3103 pelmDisk->setAttribute("ovf:format", "http://www.vmware.com/specifications/vmdk.html#compressed");
3104 }
3105
3106 // now go write the XML
3107 xml::XmlFileWriter writer(doc);
3108 writer.write(pAppliance->m->strPath.c_str());
3109 }
3110 catch(xml::Error &x)
3111 {
3112 rc = setError(VBOX_E_FILE_ERROR,
3113 x.what());
3114 }
3115 catch(HRESULT aRC)
3116 {
3117 rc = aRC;
3118 }
3119
3120 task->rc = rc;
3121
3122 if (!task->progress.isNull())
3123 task->progress->notifyComplete(rc);
3124
3125 LogFlowFunc(("rc=%Rhrc\n", rc));
3126 LogFlowFuncLeave();
3127
3128 return VINF_SUCCESS;
3129}
3130
3131/**
3132* Public method implementation.
3133 * @return
3134 */
3135STDMETHODIMP Appliance::GetWarnings(ComSafeArrayOut(BSTR, aWarnings))
3136{
3137 if (ComSafeArrayOutIsNull(aWarnings))
3138 return E_POINTER;
3139
3140 AutoCaller autoCaller(this);
3141 CheckComRCReturnRC(autoCaller.rc());
3142
3143 AutoReadLock alock(this);
3144
3145 com::SafeArray<BSTR> sfaWarnings(m->llWarnings.size());
3146
3147 list<Utf8Str>::const_iterator it;
3148 size_t i = 0;
3149 for (it = m->llWarnings.begin();
3150 it != m->llWarnings.end();
3151 ++it, ++i)
3152 {
3153 Bstr bstr = *it;
3154 bstr.cloneTo(&sfaWarnings[i]);
3155 }
3156
3157 sfaWarnings.detachTo(ComSafeArrayOutArg(aWarnings));
3158
3159 return S_OK;
3160}
3161
3162HRESULT Appliance::searchUniqueVMName(Utf8Str& aName) const
3163{
3164 IMachine *machine = NULL;
3165 char *tmpName = RTStrDup(aName.c_str());
3166 int i = 1;
3167 /* @todo: Maybe too cost-intensive; try to find a lighter way */
3168 while (mVirtualBox->FindMachine(Bstr(tmpName), &machine) != VBOX_E_OBJECT_NOT_FOUND)
3169 {
3170 RTStrFree(tmpName);
3171 RTStrAPrintf(&tmpName, "%s_%d", aName.c_str(), i);
3172 ++i;
3173 }
3174 aName = tmpName;
3175 RTStrFree(tmpName);
3176
3177 return S_OK;
3178}
3179
3180HRESULT Appliance::searchUniqueDiskImageFilePath(Utf8Str& aName) const
3181{
3182 IHardDisk *harddisk = NULL;
3183 char *tmpName = RTStrDup(aName.c_str());
3184 int i = 1;
3185 /* Check if the file exists or if a file with this path is registered
3186 * already */
3187 /* @todo: Maybe too cost-intensive; try to find a lighter way */
3188 while (RTPathExists(tmpName) ||
3189 mVirtualBox->FindHardDisk(Bstr(tmpName), &harddisk) != VBOX_E_OBJECT_NOT_FOUND)
3190 {
3191 RTStrFree(tmpName);
3192 char *tmpDir = RTStrDup(aName.c_str());
3193 RTPathStripFilename(tmpDir);;
3194 char *tmpFile = RTStrDup(RTPathFilename(aName.c_str()));
3195 RTPathStripExt(tmpFile);
3196 const char *tmpExt = RTPathExt(aName.c_str());
3197 RTStrAPrintf(&tmpName, "%s%c%s_%d%s", tmpDir, RTPATH_DELIMITER, tmpFile, i, tmpExt);
3198 RTStrFree(tmpFile);
3199 RTStrFree(tmpDir);
3200 ++i;
3201 }
3202 aName = tmpName;
3203 RTStrFree(tmpName);
3204
3205 return S_OK;
3206}
3207
3208/**
3209 * Calculates the maximum progress value for importMachines() and write().
3210 * @return
3211 */
3212uint32_t Appliance::calcMaxProgress()
3213{
3214 /* Figure out how many sub operation the import will need */
3215 /* One for the appliance */
3216 uint32_t opCount = 1;
3217 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;
3218 for (it = m->virtualSystemDescriptions.begin();
3219 it != m->virtualSystemDescriptions.end();
3220 ++it)
3221 {
3222 /* One for every Virtual System */
3223 ++opCount;
3224 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);
3225 /* One for every hard disk of the Virtual System */
3226 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);
3227 opCount += (uint32_t)avsdeHDs.size();
3228 }
3229
3230 return opCount;
3231}
3232
3233void Appliance::addWarning(const char* aWarning, ...)
3234{
3235 va_list args;
3236 va_start(args, aWarning);
3237 Utf8StrFmtVA str(aWarning, args);
3238 va_end(args);
3239 m->llWarnings.push_back(str);
3240}
3241
3242////////////////////////////////////////////////////////////////////////////////
3243//
3244// IVirtualSystemDescription constructor / destructor
3245//
3246////////////////////////////////////////////////////////////////////////////////
3247
3248DEFINE_EMPTY_CTOR_DTOR(VirtualSystemDescription)
3249struct shutup3 {};
3250
3251/**
3252 * COM initializer.
3253 * @return
3254 */
3255HRESULT VirtualSystemDescription::init()
3256{
3257 /* Enclose the state transition NotReady->InInit->Ready */
3258 AutoInitSpan autoInitSpan(this);
3259 AssertReturn(autoInitSpan.isOk(), E_FAIL);
3260
3261 /* Initialize data */
3262 m = new Data();
3263
3264 /* Confirm a successful initialization */
3265 autoInitSpan.setSucceeded();
3266 return S_OK;
3267}
3268
3269/**
3270* COM uninitializer.
3271*/
3272
3273void VirtualSystemDescription::uninit()
3274{
3275 delete m;
3276 m = NULL;
3277}
3278
3279////////////////////////////////////////////////////////////////////////////////
3280//
3281// IVirtualSystemDescription public methods
3282//
3283////////////////////////////////////////////////////////////////////////////////
3284
3285/**
3286 * Public method implementation.
3287 * @param
3288 * @return
3289 */
3290STDMETHODIMP VirtualSystemDescription::COMGETTER(Count)(ULONG *aCount)
3291{
3292 if (!aCount)
3293 return E_POINTER;
3294
3295 AutoCaller autoCaller(this);
3296 CheckComRCReturnRC(autoCaller.rc());
3297
3298 AutoReadLock alock(this);
3299
3300 *aCount = (ULONG)m->llDescriptions.size();
3301
3302 return S_OK;
3303}
3304
3305/**
3306 * Public method implementation.
3307 * @return
3308 */
3309STDMETHODIMP VirtualSystemDescription::GetDescription(ComSafeArrayOut(VirtualSystemDescriptionType_T, aTypes),
3310 ComSafeArrayOut(BSTR, aRefs),
3311 ComSafeArrayOut(BSTR, aOrigValues),
3312 ComSafeArrayOut(BSTR, aVboxValues),
3313 ComSafeArrayOut(BSTR, aExtraConfigValues))
3314{
3315 if (ComSafeArrayOutIsNull(aTypes) ||
3316 ComSafeArrayOutIsNull(aRefs) ||
3317 ComSafeArrayOutIsNull(aOrigValues) ||
3318 ComSafeArrayOutIsNull(aVboxValues) ||
3319 ComSafeArrayOutIsNull(aExtraConfigValues))
3320 return E_POINTER;
3321
3322 AutoCaller autoCaller(this);
3323 CheckComRCReturnRC(autoCaller.rc());
3324
3325 AutoReadLock alock(this);
3326
3327 ULONG c = (ULONG)m->llDescriptions.size();
3328 com::SafeArray<VirtualSystemDescriptionType_T> sfaTypes(c);
3329 com::SafeArray<BSTR> sfaRefs(c);
3330 com::SafeArray<BSTR> sfaOrigValues(c);
3331 com::SafeArray<BSTR> sfaVboxValues(c);
3332 com::SafeArray<BSTR> sfaExtraConfigValues(c);
3333
3334 list<VirtualSystemDescriptionEntry>::const_iterator it;
3335 size_t i = 0;
3336 for (it = m->llDescriptions.begin();
3337 it != m->llDescriptions.end();
3338 ++it, ++i)
3339 {
3340 const VirtualSystemDescriptionEntry &vsde = (*it);
3341
3342 sfaTypes[i] = vsde.type;
3343
3344 Bstr bstr = vsde.strRef;
3345 bstr.cloneTo(&sfaRefs[i]);
3346
3347 bstr = vsde.strOvf;
3348 bstr.cloneTo(&sfaOrigValues[i]);
3349
3350 bstr = vsde.strVbox;
3351 bstr.cloneTo(&sfaVboxValues[i]);
3352
3353 bstr = vsde.strExtraConfig;
3354 bstr.cloneTo(&sfaExtraConfigValues[i]);
3355 }
3356
3357 sfaTypes.detachTo(ComSafeArrayOutArg(aTypes));
3358 sfaRefs.detachTo(ComSafeArrayOutArg(aRefs));
3359 sfaOrigValues.detachTo(ComSafeArrayOutArg(aOrigValues));
3360 sfaVboxValues.detachTo(ComSafeArrayOutArg(aVboxValues));
3361 sfaExtraConfigValues.detachTo(ComSafeArrayOutArg(aExtraConfigValues));
3362
3363 return S_OK;
3364}
3365
3366/**
3367 * Public method implementation.
3368 * @return
3369 */
3370STDMETHODIMP VirtualSystemDescription::GetDescriptionByType(VirtualSystemDescriptionType_T aType,
3371 ComSafeArrayOut(VirtualSystemDescriptionType_T, aTypes),
3372 ComSafeArrayOut(BSTR, aRefs),
3373 ComSafeArrayOut(BSTR, aOrigValues),
3374 ComSafeArrayOut(BSTR, aVboxValues),
3375 ComSafeArrayOut(BSTR, aExtraConfigValues))
3376{
3377 if (ComSafeArrayOutIsNull(aTypes) ||
3378 ComSafeArrayOutIsNull(aRefs) ||
3379 ComSafeArrayOutIsNull(aOrigValues) ||
3380 ComSafeArrayOutIsNull(aVboxValues) ||
3381 ComSafeArrayOutIsNull(aExtraConfigValues))
3382 return E_POINTER;
3383
3384 AutoCaller autoCaller(this);
3385 CheckComRCReturnRC(autoCaller.rc());
3386
3387 AutoReadLock alock(this);
3388
3389 std::list<VirtualSystemDescriptionEntry*> vsd = findByType (aType);
3390 ULONG c = (ULONG)vsd.size();
3391 com::SafeArray<VirtualSystemDescriptionType_T> sfaTypes(c);
3392 com::SafeArray<BSTR> sfaRefs(c);
3393 com::SafeArray<BSTR> sfaOrigValues(c);
3394 com::SafeArray<BSTR> sfaVboxValues(c);
3395 com::SafeArray<BSTR> sfaExtraConfigValues(c);
3396
3397 list<VirtualSystemDescriptionEntry*>::const_iterator it;
3398 size_t i = 0;
3399 for (it = vsd.begin();
3400 it != vsd.end();
3401 ++it, ++i)
3402 {
3403 const VirtualSystemDescriptionEntry *vsde = (*it);
3404
3405 sfaTypes[i] = vsde->type;
3406
3407 Bstr bstr = vsde->strRef;
3408 bstr.cloneTo(&sfaRefs[i]);
3409
3410 bstr = vsde->strOvf;
3411 bstr.cloneTo(&sfaOrigValues[i]);
3412
3413 bstr = vsde->strVbox;
3414 bstr.cloneTo(&sfaVboxValues[i]);
3415
3416 bstr = vsde->strExtraConfig;
3417 bstr.cloneTo(&sfaExtraConfigValues[i]);
3418 }
3419
3420 sfaTypes.detachTo(ComSafeArrayOutArg(aTypes));
3421 sfaRefs.detachTo(ComSafeArrayOutArg(aRefs));
3422 sfaOrigValues.detachTo(ComSafeArrayOutArg(aOrigValues));
3423 sfaVboxValues.detachTo(ComSafeArrayOutArg(aVboxValues));
3424 sfaExtraConfigValues.detachTo(ComSafeArrayOutArg(aExtraConfigValues));
3425
3426 return S_OK;
3427}
3428
3429/**
3430 * Public method implementation.
3431 * @return
3432 */
3433STDMETHODIMP VirtualSystemDescription::GetValuesByType(VirtualSystemDescriptionType_T aType,
3434 VirtualSystemDescriptionValueType_T aWhich,
3435 ComSafeArrayOut(BSTR, aValues))
3436{
3437 if (ComSafeArrayOutIsNull(aValues))
3438 return E_POINTER;
3439
3440 AutoCaller autoCaller(this);
3441 CheckComRCReturnRC(autoCaller.rc());
3442
3443 AutoReadLock alock(this);
3444
3445 std::list<VirtualSystemDescriptionEntry*> vsd = findByType (aType);
3446 com::SafeArray<BSTR> sfaValues((ULONG)vsd.size());
3447
3448 list<VirtualSystemDescriptionEntry*>::const_iterator it;
3449 size_t i = 0;
3450 for (it = vsd.begin();
3451 it != vsd.end();
3452 ++it, ++i)
3453 {
3454 const VirtualSystemDescriptionEntry *vsde = (*it);
3455
3456 Bstr bstr;
3457 switch (aWhich)
3458 {
3459 case VirtualSystemDescriptionValueType_Reference: bstr = vsde->strRef; break;
3460 case VirtualSystemDescriptionValueType_Original: bstr = vsde->strOvf; break;
3461 case VirtualSystemDescriptionValueType_Auto: bstr = vsde->strVbox; break;
3462 case VirtualSystemDescriptionValueType_ExtraConfig: bstr = vsde->strExtraConfig; break;
3463 }
3464
3465 bstr.cloneTo(&sfaValues[i]);
3466 }
3467
3468 sfaValues.detachTo(ComSafeArrayOutArg(aValues));
3469
3470 return S_OK;
3471}
3472
3473/**
3474 * Public method implementation.
3475 * @return
3476 */
3477STDMETHODIMP VirtualSystemDescription::SetFinalValues(ComSafeArrayIn(BOOL, aEnabled),
3478 ComSafeArrayIn(IN_BSTR, argVboxValues),
3479 ComSafeArrayIn(IN_BSTR, argExtraConfigValues))
3480{
3481#ifndef RT_OS_WINDOWS
3482 NOREF(aEnabledSize);
3483#endif /* RT_OS_WINDOWS */
3484
3485 CheckComArgSafeArrayNotNull(aEnabled);
3486 CheckComArgSafeArrayNotNull(argVboxValues);
3487 CheckComArgSafeArrayNotNull(argExtraConfigValues);
3488
3489 AutoCaller autoCaller(this);
3490 CheckComRCReturnRC(autoCaller.rc());
3491
3492 AutoWriteLock alock(this);
3493
3494 com::SafeArray<IN_BSTR> aVboxValues(ComSafeArrayInArg(argVboxValues));
3495 com::SafeArray<IN_BSTR> aExtraConfigValues(ComSafeArrayInArg(argExtraConfigValues));
3496
3497 if ( (aVboxValues.size() != m->llDescriptions.size())
3498 || (aExtraConfigValues.size() != m->llDescriptions.size())
3499 )
3500 return E_INVALIDARG;
3501
3502 list<VirtualSystemDescriptionEntry>::iterator it;
3503 size_t i = 0;
3504 for (it = m->llDescriptions.begin();
3505 it != m->llDescriptions.end();
3506 ++it, ++i)
3507 {
3508 VirtualSystemDescriptionEntry& vsde = *it;
3509
3510 if (aEnabled[i])
3511 {
3512 vsde.strVbox = aVboxValues[i];
3513 vsde.strExtraConfig = aExtraConfigValues[i];
3514 }
3515 else
3516 vsde.type = VirtualSystemDescriptionType_Ignore;
3517 }
3518
3519 return S_OK;
3520}
3521
3522/**
3523 * Public method implementation.
3524 * @return
3525 */
3526STDMETHODIMP VirtualSystemDescription::AddDescription(VirtualSystemDescriptionType_T aType,
3527 IN_BSTR aVboxValue,
3528 IN_BSTR aExtraConfigValue)
3529{
3530 CheckComArgNotNull(aVboxValue);
3531 CheckComArgNotNull(aExtraConfigValue);
3532
3533 AutoCaller autoCaller(this);
3534 CheckComRCReturnRC(autoCaller.rc());
3535
3536 AutoWriteLock alock(this);
3537
3538 addEntry(aType, "", aVboxValue, aVboxValue, aExtraConfigValue);
3539
3540 return S_OK;
3541}
3542
3543/**
3544 * Internal method; adds a new description item to the member list.
3545 * @param aType Type of description for the new item.
3546 * @param strRef Reference item; only used with hard disk controllers.
3547 * @param aOrigValue Corresponding original value from OVF.
3548 * @param aAutoValue Initial configuration value (can be overridden by caller with setFinalValues).
3549 * @param strExtraConfig Extra configuration; meaning dependent on type.
3550 */
3551void VirtualSystemDescription::addEntry(VirtualSystemDescriptionType_T aType,
3552 const Utf8Str &strRef,
3553 const Utf8Str &aOrigValue,
3554 const Utf8Str &aAutoValue,
3555 const Utf8Str &strExtraConfig /*= ""*/)
3556{
3557 VirtualSystemDescriptionEntry vsde;
3558 vsde.ulIndex = (uint32_t)m->llDescriptions.size(); // each entry gets an index so the client side can reference them
3559 vsde.type = aType;
3560 vsde.strRef = strRef;
3561 vsde.strOvf = aOrigValue;
3562 vsde.strVbox = aAutoValue;
3563 vsde.strExtraConfig = strExtraConfig;
3564
3565 m->llDescriptions.push_back(vsde);
3566}
3567
3568/**
3569 * Private method; returns a list of description items containing all the items from the member
3570 * description items of this virtual system that match the given type.
3571 * @param aType
3572 * @return
3573 */
3574std::list<VirtualSystemDescriptionEntry*> VirtualSystemDescription::findByType(VirtualSystemDescriptionType_T aType)
3575{
3576 std::list<VirtualSystemDescriptionEntry*> vsd;
3577
3578 list<VirtualSystemDescriptionEntry>::iterator it;
3579 for (it = m->llDescriptions.begin();
3580 it != m->llDescriptions.end();
3581 ++it)
3582 {
3583 if (it->type == aType)
3584 vsd.push_back(&(*it));
3585 }
3586
3587 return vsd;
3588}
3589
3590/**
3591 * Private method; looks thru the member hardware items for the IDE, SATA, or SCSI controller with
3592 * the given reference ID. Useful when needing the controller for a particular
3593 * virtual disk.
3594 * @param id
3595 * @return
3596 */
3597const VirtualSystemDescriptionEntry* VirtualSystemDescription::findControllerFromID(uint32_t id)
3598{
3599 Utf8Str strRef = Utf8StrFmt("%RI32", id);
3600 list<VirtualSystemDescriptionEntry>::const_iterator it;
3601 for (it = m->llDescriptions.begin();
3602 it != m->llDescriptions.end();
3603 ++it)
3604 {
3605 const VirtualSystemDescriptionEntry &d = *it;
3606 switch (d.type)
3607 {
3608 case VirtualSystemDescriptionType_HardDiskControllerIDE:
3609 case VirtualSystemDescriptionType_HardDiskControllerSATA:
3610 case VirtualSystemDescriptionType_HardDiskControllerSCSI:
3611 if (d.strRef == strRef)
3612 return &d;
3613 break;
3614 }
3615 }
3616
3617 return NULL;
3618}
3619
3620////////////////////////////////////////////////////////////////////////////////
3621//
3622// IMachine public methods
3623//
3624////////////////////////////////////////////////////////////////////////////////
3625
3626// This code is here so we won't have to include the appliance headers in the
3627// IMachine implementation, and we also need to access private appliance data.
3628
3629/**
3630* Public method implementation.
3631* @param appliance
3632* @return
3633*/
3634
3635STDMETHODIMP Machine::Export(IAppliance *aAppliance, IVirtualSystemDescription **aDescription)
3636{
3637 HRESULT rc = S_OK;
3638
3639 if (!aAppliance)
3640 return E_POINTER;
3641
3642 AutoCaller autoCaller(this);
3643 CheckComRCReturnRC(autoCaller.rc());
3644
3645 AutoReadLock alock(this);
3646
3647 ComObjPtr<VirtualSystemDescription> pNewDesc;
3648
3649 try
3650 {
3651 Bstr bstrName;
3652 Bstr bstrDescription;
3653 Bstr bstrGuestOSType;
3654 uint32_t cCPUs;
3655 uint32_t ulMemSizeMB;
3656 BOOL fDVDEnabled;
3657 BOOL fFloppyEnabled;
3658 BOOL fUSBEnabled;
3659 BOOL fAudioEnabled;
3660 AudioControllerType_T audioController;
3661
3662 ComPtr<IUSBController> pUsbController;
3663 ComPtr<IAudioAdapter> pAudioAdapter;
3664
3665 // get name
3666 bstrName = mUserData->mName;
3667 // get description
3668 bstrDescription = mUserData->mDescription;
3669 // get guest OS
3670 bstrGuestOSType = mUserData->mOSTypeId;
3671 // CPU count
3672 cCPUs = mHWData->mCPUCount;
3673 // memory size in MB
3674 ulMemSizeMB = mHWData->mMemorySize;
3675 // VRAM size?
3676 // BIOS settings?
3677 // 3D acceleration enabled?
3678 // hardware virtualization enabled?
3679 // nested paging enabled?
3680 // HWVirtExVPIDEnabled?
3681 // PAEEnabled?
3682 // snapshotFolder?
3683 // VRDPServer?
3684
3685 // floppy
3686 rc = mFloppyDrive->COMGETTER(Enabled)(&fFloppyEnabled);
3687 if (FAILED(rc)) throw rc;
3688
3689 // CD-ROM ?!?
3690 // ComPtr<IDVDDrive> pDVDDrive;
3691 fDVDEnabled = 1;
3692
3693 // this is more tricky so use the COM method
3694 rc = COMGETTER(USBController)(pUsbController.asOutParam());
3695 if (FAILED(rc)) throw rc;
3696 rc = pUsbController->COMGETTER(Enabled)(&fUSBEnabled);
3697
3698 pAudioAdapter = mAudioAdapter;
3699 rc = pAudioAdapter->COMGETTER(Enabled)(&fAudioEnabled);
3700 if (FAILED(rc)) throw rc;
3701 rc = pAudioAdapter->COMGETTER(AudioController)(&audioController);
3702 if (FAILED(rc)) throw rc;
3703
3704 // create a new virtual system
3705 rc = pNewDesc.createObject();
3706 CheckComRCThrowRC(rc);
3707 rc = pNewDesc->init();
3708 CheckComRCThrowRC(rc);
3709
3710 /* Guest OS type */
3711 Utf8Str strOsTypeVBox(bstrGuestOSType);
3712 CIMOSType_T cim = convertVBoxOSType2CIMOSType(strOsTypeVBox.c_str());
3713 pNewDesc->addEntry(VirtualSystemDescriptionType_OS,
3714 "",
3715 Utf8StrFmt("%RI32", cim),
3716 strOsTypeVBox);
3717
3718 /* VM name */
3719 Utf8Str strVMName(bstrName);
3720 pNewDesc->addEntry(VirtualSystemDescriptionType_Name,
3721 "",
3722 strVMName,
3723 strVMName);
3724
3725 // description
3726 Utf8Str strDescription(bstrDescription);
3727 pNewDesc->addEntry(VirtualSystemDescriptionType_Description,
3728 "",
3729 strDescription,
3730 strDescription);
3731
3732 /* CPU count*/
3733 Utf8Str strCpuCount = Utf8StrFmt("%RI32", cCPUs);
3734 pNewDesc->addEntry(VirtualSystemDescriptionType_CPU,
3735 "",
3736 strCpuCount,
3737 strCpuCount);
3738
3739 /* Memory */
3740 Utf8Str strMemory = Utf8StrFmt("%RI32", (uint64_t)ulMemSizeMB * _1M);
3741 pNewDesc->addEntry(VirtualSystemDescriptionType_Memory,
3742 "",
3743 strMemory,
3744 strMemory);
3745
3746 int32_t lIDEControllerIndex = 0;
3747 int32_t lSATAControllerIndex = 0;
3748 int32_t lSCSIControllerIndex = 0;
3749
3750// <const name="HardDiskControllerIDE" value="6" />
3751 ComPtr<IStorageController> pController;
3752 rc = GetStorageControllerByName(Bstr("IDE"), pController.asOutParam());
3753 if (FAILED(rc)) throw rc;
3754 Utf8Str strVbox;
3755 StorageControllerType_T ctlr;
3756 rc = pController->COMGETTER(ControllerType)(&ctlr);
3757 if (FAILED(rc)) throw rc;
3758 switch(ctlr)
3759 {
3760 case StorageControllerType_PIIX3: strVbox = "PIIX3"; break;
3761 case StorageControllerType_PIIX4: strVbox = "PIIX4"; break;
3762 case StorageControllerType_ICH6: strVbox = "ICH6"; break;
3763 }
3764
3765 if (strVbox.length())
3766 {
3767 lIDEControllerIndex = (int32_t)pNewDesc->m->llDescriptions.size();
3768 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerIDE,
3769 Utf8StrFmt("%d", lIDEControllerIndex),
3770 strVbox,
3771 strVbox);
3772 }
3773
3774#ifdef VBOX_WITH_AHCI
3775// <const name="HardDiskControllerSATA" value="7" />
3776 rc = GetStorageControllerByName(Bstr("SATA"), pController.asOutParam());
3777 if (SUCCEEDED(rc))
3778 {
3779 strVbox = "AHCI";
3780 lSATAControllerIndex = (int32_t)pNewDesc->m->llDescriptions.size();
3781 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSATA,
3782 Utf8StrFmt("%d", lSATAControllerIndex),
3783 strVbox,
3784 strVbox);
3785 }
3786#endif // VBOX_WITH_AHCI
3787
3788// <const name="HardDiskControllerSCSI" value="8" />
3789 rc = GetStorageControllerByName(Bstr("SCSI"), pController.asOutParam());
3790 if (SUCCEEDED(rc))
3791 {
3792 rc = pController->COMGETTER(ControllerType)(&ctlr);
3793 if (SUCCEEDED(rc))
3794 {
3795 strVbox = "LsiLogic"; // the default in VBox
3796 switch(ctlr)
3797 {
3798 case StorageControllerType_LsiLogic: strVbox = "LsiLogic"; break;
3799 case StorageControllerType_BusLogic: strVbox = "BusLogic"; break;
3800 }
3801 lSCSIControllerIndex = (int32_t)pNewDesc->m->llDescriptions.size();
3802 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSCSI,
3803 Utf8StrFmt("%d", lSCSIControllerIndex),
3804 strVbox,
3805 strVbox);
3806 }
3807 else
3808 throw rc;
3809 }
3810
3811// <const name="HardDiskImage" value="9" />
3812 HDData::AttachmentList::iterator itA;
3813 for (itA = mHDData->mAttachments.begin();
3814 itA != mHDData->mAttachments.end();
3815 ++itA)
3816 {
3817 ComObjPtr<HardDiskAttachment> pHDA = *itA;
3818
3819 // the attachment's data
3820 ComPtr<IHardDisk> pHardDisk;
3821 ComPtr<IStorageController> ctl;
3822 Bstr controllerName;
3823
3824 rc = pHDA->COMGETTER(Controller)(controllerName.asOutParam());
3825 if (FAILED(rc)) throw rc;
3826
3827 rc = GetStorageControllerByName(controllerName, ctl.asOutParam());
3828 if (FAILED(rc)) throw rc;
3829
3830 StorageBus_T storageBus;
3831 LONG lChannel;
3832 LONG lDevice;
3833
3834 rc = ctl->COMGETTER(Bus)(&storageBus);
3835 if (FAILED(rc)) throw rc;
3836
3837 rc = pHDA->COMGETTER(HardDisk)(pHardDisk.asOutParam());
3838 if (FAILED(rc)) throw rc;
3839
3840 rc = pHDA->COMGETTER(Port)(&lChannel);
3841 if (FAILED(rc)) throw rc;
3842
3843 rc = pHDA->COMGETTER(Device)(&lDevice);
3844 if (FAILED(rc)) throw rc;
3845
3846 Bstr bstrLocation;
3847 rc = pHardDisk->COMGETTER(Location)(bstrLocation.asOutParam());
3848 Bstr bstrName;
3849 rc = pHardDisk->COMGETTER(Name)(bstrName.asOutParam());
3850
3851 // and how this translates to the virtual system
3852 int32_t lControllerVsys = 0;
3853 LONG lChannelVsys;
3854
3855 switch (storageBus)
3856 {
3857 case StorageBus_IDE:
3858 // this is the exact reverse to what we're doing in Appliance::taskThreadImportMachines,
3859 // and it must be updated when that is changed!
3860
3861 if (lChannel == 0 && lDevice == 0) // primary master
3862 lChannelVsys = 0;
3863 else if (lChannel == 0 && lDevice == 1) // primary slave
3864 lChannelVsys = 1;
3865 else if (lChannel == 1 && lDevice == 1) // secondary slave; secondary master is always CDROM
3866 lChannelVsys = 2;
3867 else
3868 throw setError(VBOX_E_NOT_SUPPORTED,
3869 tr("Cannot handle hard disk attachment: channel is %d, device is %d"), lChannel, lDevice);
3870
3871 lControllerVsys = lIDEControllerIndex;
3872 break;
3873
3874 case StorageBus_SATA:
3875 lChannelVsys = lChannel; // should be between 0 and 29
3876 lControllerVsys = lSATAControllerIndex;
3877 break;
3878
3879 case StorageBus_SCSI:
3880 lChannelVsys = lChannel; // should be between 0 and 15
3881 lControllerVsys = lSCSIControllerIndex;
3882 break;
3883
3884 default:
3885 throw setError(VBOX_E_NOT_SUPPORTED,
3886 tr("Cannot handle hard disk attachment: storageBus is %d, channel is %d, device is %d"), storageBus, lChannel, lDevice);
3887 break;
3888 }
3889
3890 Utf8Str strTargetVmdkName(bstrName);
3891 RTPathStripExt(strTargetVmdkName.mutableRaw());
3892 strTargetVmdkName.append(".vmdk");
3893
3894 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskImage,
3895 strTargetVmdkName, // disk ID: let's use the name
3896 strTargetVmdkName, // OVF value:
3897 Utf8Str(bstrLocation), // vbox value: media path
3898 Utf8StrFmt("controller=%RI32;channel=%RI32", lControllerVsys, lChannelVsys));
3899 }
3900
3901 /* Floppy Drive */
3902 if (fFloppyEnabled)
3903 pNewDesc->addEntry(VirtualSystemDescriptionType_Floppy, "", "", "");
3904
3905 /* CD Drive */
3906 if (fDVDEnabled)
3907 pNewDesc->addEntry(VirtualSystemDescriptionType_CDROM, "", "", "");
3908
3909// <const name="NetworkAdapter" />
3910 size_t a;
3911 for (a = 0;
3912 a < SchemaDefs::NetworkAdapterCount;
3913 ++a)
3914 {
3915 ComPtr<INetworkAdapter> pNetworkAdapter;
3916 BOOL fEnabled;
3917 NetworkAdapterType_T adapterType;
3918 NetworkAttachmentType_T attachmentType;
3919
3920 rc = GetNetworkAdapter((ULONG)a, pNetworkAdapter.asOutParam());
3921 if (FAILED(rc)) throw rc;
3922 /* Enable the network card & set the adapter type */
3923 rc = pNetworkAdapter->COMGETTER(Enabled)(&fEnabled);
3924 if (FAILED(rc)) throw rc;
3925
3926 if (fEnabled)
3927 {
3928 Utf8Str strAttachmentType;
3929
3930 rc = pNetworkAdapter->COMGETTER(AdapterType)(&adapterType);
3931 if (FAILED(rc)) throw rc;
3932
3933 rc = pNetworkAdapter->COMGETTER(AttachmentType)(&attachmentType);
3934 if (FAILED(rc)) throw rc;
3935
3936 switch (attachmentType)
3937 {
3938 case NetworkAttachmentType_Null:
3939 strAttachmentType = "Null";
3940 break;
3941
3942 case NetworkAttachmentType_NAT:
3943 strAttachmentType = "NAT";
3944 break;
3945
3946 case NetworkAttachmentType_Bridged:
3947 strAttachmentType = "Bridged";
3948 break;
3949
3950 case NetworkAttachmentType_Internal:
3951 strAttachmentType = "Internal";
3952 break;
3953
3954 case NetworkAttachmentType_HostOnly:
3955 strAttachmentType = "HostOnly";
3956 break;
3957 }
3958
3959 pNewDesc->addEntry(VirtualSystemDescriptionType_NetworkAdapter,
3960 "", // ref
3961 strAttachmentType, // orig
3962 Utf8StrFmt("%RI32", (uint32_t)adapterType), // conf
3963 Utf8StrFmt("type=%s", strAttachmentType.c_str())); // extra conf
3964 }
3965 }
3966
3967// <const name="USBController" />
3968#ifdef VBOX_WITH_USB
3969 if (fUSBEnabled)
3970 pNewDesc->addEntry(VirtualSystemDescriptionType_USBController, "", "", "");
3971#endif /* VBOX_WITH_USB */
3972
3973// <const name="SoundCard" />
3974 if (fAudioEnabled)
3975 {
3976 pNewDesc->addEntry(VirtualSystemDescriptionType_SoundCard,
3977 "",
3978 "ensoniq1371", // this is what OVFTool writes and VMware supports
3979 Utf8StrFmt("%RI32", audioController));
3980 }
3981
3982 // finally, add the virtual system to the appliance
3983 Appliance *pAppliance = static_cast<Appliance*>(aAppliance);
3984 AutoCaller autoCaller(pAppliance);
3985 if (FAILED(rc)) throw rc;
3986
3987 /* We return the new description to the caller */
3988 ComPtr<IVirtualSystemDescription> copy(pNewDesc);
3989 copy.queryInterfaceTo(aDescription);
3990
3991 AutoWriteLock alock(pAppliance);
3992
3993 pAppliance->m->virtualSystemDescriptions.push_back(pNewDesc);
3994 }
3995 catch(HRESULT arc)
3996 {
3997 rc = arc;
3998 }
3999
4000 return rc;
4001}
4002
4003/* vi: set tabstop=4 shiftwidth=4 expandtab: */
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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