VirtualBox

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

Last change on this file since 18348 was 18340, checked in by vboxsync, 16 years ago

OVF: default to bridged networking on import as expected by vmware

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 174.2 KB
Line 
1/* $Id: ApplianceImpl.cpp 18340 2009-03-26 18:24:01Z 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.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
19 * Clara, CA 95054 USA or visit http://www.sun.com if you need
20 * additional information or have any questions.
21 */
22
23#include <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; // optional 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; -1 if not set)
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 ULONG ulWeightPerOperation; // for progress calculations
214};
215
216struct VirtualSystemDescription::Data
217{
218 list<VirtualSystemDescriptionEntry> llDescriptions;
219};
220
221////////////////////////////////////////////////////////////////////////////////
222//
223// internal helpers
224//
225////////////////////////////////////////////////////////////////////////////////
226
227static Utf8Str stripFilename(const Utf8Str &strFile)
228{
229 Utf8Str str2(strFile);
230 RTPathStripFilename(str2.mutableRaw());
231 return str2;
232}
233
234static const struct
235{
236 CIMOSType_T cim;
237 const char *pcszVbox;
238}
239 g_osTypes[] =
240 {
241 { CIMOSType_CIMOS_Unknown, SchemaDefs_OSTypeId_Other },
242 { CIMOSType_CIMOS_OS2, SchemaDefs_OSTypeId_OS2 },
243 { CIMOSType_CIMOS_MSDOS, SchemaDefs_OSTypeId_DOS },
244 { CIMOSType_CIMOS_WIN3x, SchemaDefs_OSTypeId_Windows31 },
245 { CIMOSType_CIMOS_WIN95, SchemaDefs_OSTypeId_Windows95 },
246 { CIMOSType_CIMOS_WIN98, SchemaDefs_OSTypeId_Windows98 },
247 { CIMOSType_CIMOS_WINNT, SchemaDefs_OSTypeId_WindowsNT4 },
248 { CIMOSType_CIMOS_NetWare, SchemaDefs_OSTypeId_Netware },
249 { CIMOSType_CIMOS_NovellOES, SchemaDefs_OSTypeId_Netware },
250 { CIMOSType_CIMOS_Solaris, SchemaDefs_OSTypeId_OpenSolaris },
251 { CIMOSType_CIMOS_SunOS, SchemaDefs_OSTypeId_OpenSolaris },
252 { CIMOSType_CIMOS_FreeBSD, SchemaDefs_OSTypeId_FreeBSD },
253 { CIMOSType_CIMOS_NetBSD, SchemaDefs_OSTypeId_NetBSD },
254 { CIMOSType_CIMOS_QNX, SchemaDefs_OSTypeId_QNX },
255 { CIMOSType_CIMOS_Windows2000, SchemaDefs_OSTypeId_Windows2000 },
256 { CIMOSType_CIMOS_WindowsMe, SchemaDefs_OSTypeId_WindowsMe },
257 { CIMOSType_CIMOS_OpenBSD, SchemaDefs_OSTypeId_OpenBSD },
258 { CIMOSType_CIMOS_WindowsXP, SchemaDefs_OSTypeId_WindowsXP },
259 { CIMOSType_CIMOS_WindowsXPEmbedded, SchemaDefs_OSTypeId_WindowsXP },
260 { CIMOSType_CIMOS_WindowsEmbeddedforPointofService, SchemaDefs_OSTypeId_WindowsXP },
261 { CIMOSType_CIMOS_MicrosoftWindowsServer2003, SchemaDefs_OSTypeId_Windows2003 },
262 { CIMOSType_CIMOS_MicrosoftWindowsServer2003_64, SchemaDefs_OSTypeId_Windows2003_64 },
263 { CIMOSType_CIMOS_WindowsXP_64, SchemaDefs_OSTypeId_WindowsXP_64 },
264 { CIMOSType_CIMOS_WindowsVista, SchemaDefs_OSTypeId_WindowsVista },
265 { CIMOSType_CIMOS_WindowsVista_64, SchemaDefs_OSTypeId_WindowsVista_64 },
266 { CIMOSType_CIMOS_MicrosoftWindowsServer2008, SchemaDefs_OSTypeId_Windows2008 },
267 { CIMOSType_CIMOS_MicrosoftWindowsServer2008_64, SchemaDefs_OSTypeId_Windows2008_64 },
268 { CIMOSType_CIMOS_FreeBSD_64, SchemaDefs_OSTypeId_FreeBSD_64 },
269 { CIMOSType_CIMOS_RedHatEnterpriseLinux, SchemaDefs_OSTypeId_RedHat },
270 { CIMOSType_CIMOS_RedHatEnterpriseLinux_64, SchemaDefs_OSTypeId_RedHat_64 },
271 { CIMOSType_CIMOS_Solaris_64, SchemaDefs_OSTypeId_OpenSolaris_64 },
272 { CIMOSType_CIMOS_SUSE, SchemaDefs_OSTypeId_OpenSUSE },
273 { CIMOSType_CIMOS_SLES, SchemaDefs_OSTypeId_OpenSUSE },
274 { CIMOSType_CIMOS_NovellLinuxDesktop, SchemaDefs_OSTypeId_OpenSUSE },
275 { CIMOSType_CIMOS_SUSE_64, SchemaDefs_OSTypeId_OpenSUSE_64 },
276 { CIMOSType_CIMOS_SLES_64, SchemaDefs_OSTypeId_OpenSUSE_64 },
277 { CIMOSType_CIMOS_LINUX, SchemaDefs_OSTypeId_Linux },
278 { CIMOSType_CIMOS_SunJavaDesktopSystem, SchemaDefs_OSTypeId_Linux },
279 { CIMOSType_CIMOS_TurboLinux, SchemaDefs_OSTypeId_Linux},
280
281 // { CIMOSType_CIMOS_TurboLinux_64, },
282
283 { CIMOSType_CIMOS_Mandriva, SchemaDefs_OSTypeId_Mandriva },
284 { CIMOSType_CIMOS_Mandriva_64, SchemaDefs_OSTypeId_Mandriva_64 },
285 { CIMOSType_CIMOS_Ubuntu, SchemaDefs_OSTypeId_Ubuntu },
286 { CIMOSType_CIMOS_Ubuntu_64, SchemaDefs_OSTypeId_Ubuntu_64 },
287 { CIMOSType_CIMOS_Debian, SchemaDefs_OSTypeId_Debian },
288 { CIMOSType_CIMOS_Debian_64, SchemaDefs_OSTypeId_Debian_64 },
289 { CIMOSType_CIMOS_Linux_2_4_x, SchemaDefs_OSTypeId_Linux24 },
290 { CIMOSType_CIMOS_Linux_2_4_x_64, SchemaDefs_OSTypeId_Linux24_64 },
291 { CIMOSType_CIMOS_Linux_2_6_x, SchemaDefs_OSTypeId_Linux26 },
292 { CIMOSType_CIMOS_Linux_2_6_x_64, SchemaDefs_OSTypeId_Linux26_64 },
293 { CIMOSType_CIMOS_Linux_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 = "Bridged"; // VMware assumes this is the default apparently
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 ea.strNetworkName, // orig
1395 Utf8StrFmt("%RI32", (uint32_t)nwAdapterVBox), // conf
1396 0,
1397 Utf8StrFmt("type=%s", strNetwork.c_str())); // extra conf
1398 }
1399 }
1400
1401 /* Floppy Drive */
1402 if (vsysThis.fHasFloppyDrive)
1403 pNewDesc->addEntry(VirtualSystemDescriptionType_Floppy, "", "", "");
1404
1405 /* CD Drive */
1406 /* @todo: I can't disable the CDROM. So nothing to do for now */
1407 /*
1408 if (vsysThis.fHasCdromDrive)
1409 pNewDesc->addEntry(VirtualSystemDescriptionType_CDROM, "", "", "");*/
1410
1411 /* Hard disk Controller */
1412 uint16_t cIDEused = 0;
1413 uint16_t cSATAused = 0;
1414 uint16_t cSCSIused = 0;
1415 ControllersMap::const_iterator hdcIt;
1416 /* Iterate through all hard disk controllers */
1417 for (hdcIt = vsysThis.mapControllers.begin();
1418 hdcIt != vsysThis.mapControllers.end();
1419 ++hdcIt)
1420 {
1421 const HardDiskController &hdc = hdcIt->second;
1422 Utf8Str strControllerID = Utf8StrFmt("%RI32", (uint32_t)hdc.idController);
1423
1424 switch (hdc.system)
1425 {
1426 case HardDiskController::IDE:
1427 {
1428 /* Check for the constrains */
1429 /* @todo: I'm very confused! Are these bits *one* controller or
1430 is every port/bus declared as an extra controller. */
1431 if (cIDEused < 4)
1432 {
1433 // @todo: figure out the IDE types
1434 /* Use PIIX4 as default */
1435 Utf8Str strType = "PIIX4";
1436 if (!hdc.strControllerType.compare("PIIX3", Utf8Str::CaseInsensitive))
1437 strType = "PIIX3";
1438 else if (!hdc.strControllerType.compare("ICH6", Utf8Str::CaseInsensitive))
1439 strType = "ICH6";
1440 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerIDE,
1441 strControllerID,
1442 hdc.strControllerType,
1443 strType);
1444 }
1445 else
1446 {
1447 /* Warn only once */
1448 if (cIDEused == 1)
1449 addWarning(tr("The virtual \"%s\" system requests support for more than one IDE controller, but VirtualBox has support for only one."),
1450 vsysThis.strName.c_str());
1451
1452 }
1453 ++cIDEused;
1454 break;
1455 }
1456
1457#ifdef VBOX_WITH_AHCI
1458 case HardDiskController::SATA:
1459 {
1460 /* Check for the constrains */
1461 if (cSATAused < 1)
1462 {
1463 // @todo: figure out the SATA types
1464 /* We only support a plain AHCI controller, so use them always */
1465 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSATA,
1466 strControllerID,
1467 hdc.strControllerType,
1468 "AHCI");
1469 }
1470 else
1471 {
1472 /* Warn only once */
1473 if (cSATAused == 1)
1474 addWarning(tr("The virtual system \"%s\" requests support for more than one SATA controller, but VirtualBox has support for only one"),
1475 vsysThis.strName.c_str());
1476
1477 }
1478 ++cSATAused;
1479 break;
1480 }
1481#endif /* VBOX_WITH_AHCI */
1482
1483 case HardDiskController::SCSI:
1484 {
1485 /* Check for the constrains */
1486 if (cSCSIused < 1)
1487 {
1488 Utf8Str hdcController = "LsiLogic";
1489 if (!hdc.strControllerType.compare("BusLogic", Utf8Str::CaseInsensitive))
1490 hdcController = "BusLogic";
1491 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSCSI,
1492 strControllerID,
1493 hdc.strControllerType,
1494 hdcController);
1495 }
1496 else
1497 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."),
1498 vsysThis.strName.c_str(),
1499 hdc.strControllerType.c_str(),
1500 strControllerID.c_str());
1501 ++cSCSIused;
1502 break;
1503 }
1504 }
1505 }
1506
1507 /* Hard disks */
1508 if (vsysThis.mapVirtualDisks.size() > 0)
1509 {
1510 VirtualDisksMap::const_iterator itVD;
1511 /* Iterate through all hard disks ()*/
1512 for (itVD = vsysThis.mapVirtualDisks.begin();
1513 itVD != vsysThis.mapVirtualDisks.end();
1514 ++itVD)
1515 {
1516 const VirtualDisk &hd = itVD->second;
1517 /* Get the associated disk image */
1518 const DiskImage &di = m->mapDisks[hd.strDiskId];
1519
1520 // @todo:
1521 // - figure out all possible vmdk formats we also support
1522 // - figure out if there is a url specifier for vhd already
1523 // - we need a url specifier for the vdi format
1524 if ( di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#sparse", Utf8Str::CaseInsensitive)
1525 || di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#compressed", Utf8Str::CaseInsensitive))
1526 {
1527 /* If the href is empty use the VM name as filename */
1528 Utf8Str strFilename = di.strHref;
1529 if (!strFilename.length())
1530 strFilename = Utf8StrFmt("%s.vmdk", nameVBox.c_str());
1531 /* Construct a unique target path */
1532 Utf8StrFmt strPath("%ls%c%s",
1533 bstrDefaultHardDiskLocation.raw(),
1534 RTPATH_DELIMITER,
1535 strFilename.c_str());
1536 searchUniqueDiskImageFilePath(strPath);
1537
1538 /* find the description for the hard disk controller
1539 * that has the same ID as hd.idController */
1540 const VirtualSystemDescriptionEntry *pController;
1541 if (!(pController = pNewDesc->findControllerFromID(hd.idController)))
1542 throw setError(E_FAIL,
1543 tr("Cannot find hard disk controller with OVF instance ID %RI32 to which disk \"%s\" should be attached"),
1544 hd.idController,
1545 di.strHref.c_str());
1546
1547 /* controller to attach to, and the bus within that controller */
1548 Utf8StrFmt strExtraConfig("controller=%RI16;channel=%RI16",
1549 pController->ulIndex,
1550 hd.ulAddressOnParent);
1551 ULONG ulSize = 0;
1552 if (di.iPopulatedSize != -1)
1553 ulSize = (ULONG)(di.iPopulatedSize / _1M);
1554 else if (di.iSize != -1)
1555 ulSize = (ULONG)(di.iSize / _1M);
1556 else if (di.iCapacity != -1)
1557 ulSize = (ULONG)(di.iCapacity / _1M);
1558 if (ulSize == 0)
1559 ulSize = 10000; // assume 10 GB, this is for the progress bar only anyway
1560 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskImage,
1561 hd.strDiskId,
1562 di.strHref,
1563 strPath,
1564 ulSize,
1565 strExtraConfig);
1566 }
1567 else
1568 throw setError(VBOX_E_FILE_ERROR,
1569 tr("Unsupported format for virtual disk image in OVF: \"%s\"", di.strFormat.c_str()));
1570 }
1571 }
1572
1573 m->virtualSystemDescriptions.push_back(pNewDesc);
1574 }
1575 }
1576 catch (HRESULT aRC)
1577 {
1578 /* On error we clear the list & return */
1579 m->virtualSystemDescriptions.clear();
1580 rc = aRC;
1581 }
1582
1583 return rc;
1584}
1585
1586struct Appliance::TaskImportMachines
1587{
1588 TaskImportMachines(Appliance *aThat, Progress *aProgress)
1589 : pAppliance(aThat)
1590 , progress(aProgress)
1591 , rc(S_OK)
1592 {}
1593 ~TaskImportMachines() {}
1594
1595 HRESULT startThread();
1596
1597 Appliance *pAppliance;
1598 ComObjPtr<Progress> progress;
1599 HRESULT rc;
1600};
1601
1602HRESULT Appliance::TaskImportMachines::startThread()
1603{
1604 int vrc = RTThreadCreate(NULL, Appliance::taskThreadImportMachines, this,
1605 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
1606 "Appliance::Task");
1607 ComAssertMsgRCRet(vrc,
1608 ("Could not create taskThreadImportMachines (%Rrc)\n", vrc), E_FAIL);
1609
1610 return S_OK;
1611}
1612
1613/**
1614 * Public method implementation.
1615 * @param aProgress
1616 * @return
1617 */
1618STDMETHODIMP Appliance::ImportMachines(IProgress **aProgress)
1619{
1620 CheckComArgOutPointerValid(aProgress);
1621
1622 AutoCaller autoCaller(this);
1623 CheckComRCReturnRC(autoCaller.rc());
1624
1625 AutoReadLock(this);
1626
1627 HRESULT rc = S_OK;
1628
1629 ComObjPtr<Progress> progress;
1630 try
1631 {
1632 Bstr progressDesc = BstrFmt(tr("Import appliance '%s'"),
1633 m->strPath.raw());
1634 rc = setUpProgress(progress, progressDesc);
1635 if (FAILED(rc)) throw rc;
1636
1637 /* Initialize our worker task */
1638 std::auto_ptr<TaskImportMachines> task(new TaskImportMachines(this, progress));
1639 //AssertComRCThrowRC (task->autoCaller.rc());
1640
1641 rc = task->startThread();
1642 if (FAILED(rc)) throw rc;
1643
1644 task.release();
1645 }
1646 catch (HRESULT aRC)
1647 {
1648 rc = aRC;
1649 }
1650
1651 if (SUCCEEDED(rc))
1652 /* Return progress to the caller */
1653 progress.queryInterfaceTo(aProgress);
1654
1655 return rc;
1656}
1657
1658struct MyHardDiskAttachment
1659{
1660 Guid uuid;
1661 ComPtr<IMachine> pMachine;
1662 Bstr controllerType;
1663 int32_t lChannel;
1664 int32_t lDevice;
1665};
1666
1667/**
1668 * Worker thread implementation for ImportMachines().
1669 * @param aThread
1670 * @param pvUser
1671 */
1672/* static */
1673DECLCALLBACK(int) Appliance::taskThreadImportMachines(RTTHREAD /* aThread */, void *pvUser)
1674{
1675 std::auto_ptr<TaskImportMachines> task(static_cast<TaskImportMachines*>(pvUser));
1676 AssertReturn(task.get(), VERR_GENERAL_FAILURE);
1677
1678 Appliance *pAppliance = task->pAppliance;
1679
1680 LogFlowFuncEnter();
1681 LogFlowFunc(("Appliance %p\n", pAppliance));
1682
1683 AutoCaller autoCaller(pAppliance);
1684 CheckComRCReturnRC(autoCaller.rc());
1685
1686 AutoWriteLock appLock(pAppliance);
1687
1688 HRESULT rc = S_OK;
1689
1690 ComPtr<IVirtualBox> pVirtualBox(pAppliance->mVirtualBox);
1691
1692 // rollback for errors:
1693 // 1) a list of images that we created/imported
1694 list<MyHardDiskAttachment> llHardDiskAttachments;
1695 list< ComPtr<IHardDisk> > llHardDisksCreated;
1696 list<Guid> llMachinesRegistered;
1697
1698 ComPtr<ISession> session;
1699 bool fSessionOpen = false;
1700 rc = session.createInprocObject(CLSID_Session);
1701 CheckComRCReturnRC(rc);
1702
1703 list<VirtualSystem>::const_iterator it;
1704 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it1;
1705 /* Iterate through all virtual systems of that appliance */
1706 size_t i = 0;
1707 for (it = pAppliance->m->llVirtualSystems.begin(),
1708 it1 = pAppliance->m->virtualSystemDescriptions.begin();
1709 it != pAppliance->m->llVirtualSystems.end();
1710 ++it, ++it1, ++i)
1711 {
1712 const VirtualSystem &vsysThis = *it;
1713 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it1);
1714
1715 ComPtr<IMachine> pNewMachine;
1716
1717 /* Catch possible errors */
1718 try
1719 {
1720 /* Guest OS type */
1721 std::list<VirtualSystemDescriptionEntry*> vsdeOS;
1722 vsdeOS = vsdescThis->findByType(VirtualSystemDescriptionType_OS);
1723 if (vsdeOS.size() < 1)
1724 throw setError(VBOX_E_FILE_ERROR,
1725 tr("Missing guest OS type"));
1726 const Utf8Str &strOsTypeVBox = vsdeOS.front()->strVbox;
1727
1728 /* Now that we know the base system get our internal defaults based on that. */
1729 ComPtr<IGuestOSType> osType;
1730 rc = pVirtualBox->GetGuestOSType(Bstr(strOsTypeVBox), osType.asOutParam());
1731 if (FAILED(rc)) throw rc;
1732
1733 /* Create the machine */
1734 /* First get the name */
1735 std::list<VirtualSystemDescriptionEntry*> vsdeName = vsdescThis->findByType(VirtualSystemDescriptionType_Name);
1736 if (vsdeName.size() < 1)
1737 throw setError(VBOX_E_FILE_ERROR,
1738 tr("Missing VM name"));
1739 const Utf8Str &strNameVBox = vsdeName.front()->strVbox;
1740 rc = pVirtualBox->CreateMachine(Bstr(strNameVBox), Bstr(strOsTypeVBox),
1741 Bstr(), Guid(),
1742 pNewMachine.asOutParam());
1743 if (FAILED(rc)) throw rc;
1744
1745 // and the description
1746 std::list<VirtualSystemDescriptionEntry*> vsdeDescription = vsdescThis->findByType(VirtualSystemDescriptionType_Description);
1747 if (vsdeDescription.size())
1748 {
1749 const Utf8Str &strDescription = vsdeDescription.front()->strVbox;
1750 rc = pNewMachine->COMSETTER(Description)(Bstr(strDescription));
1751 if (FAILED(rc)) throw rc;
1752 }
1753
1754 /* CPU count (ignored for now) */
1755 // EntriesList vsdeCPU = vsd->findByType (VirtualSystemDescriptionType_CPU);
1756
1757 /* RAM */
1758 std::list<VirtualSystemDescriptionEntry*> vsdeRAM = vsdescThis->findByType(VirtualSystemDescriptionType_Memory);
1759 ComAssertMsgThrow(vsdeRAM.size() == 1, ("RAM size missing"), E_FAIL);
1760 const Utf8Str &memoryVBox = vsdeRAM.front()->strVbox;
1761 ULONG tt = (ULONG)RTStrToUInt64(memoryVBox.c_str());
1762 rc = pNewMachine->COMSETTER(MemorySize)(tt);
1763 if (FAILED(rc)) throw rc;
1764
1765 /* VRAM */
1766 /* Get the recommended VRAM for this guest OS type */
1767 ULONG vramVBox;
1768 rc = osType->COMGETTER(RecommendedVRAM)(&vramVBox);
1769 if (FAILED(rc)) throw rc;
1770
1771 /* Set the VRAM */
1772 rc = pNewMachine->COMSETTER(VRAMSize)(vramVBox);
1773 if (FAILED(rc)) throw rc;
1774
1775 /* Audio Adapter */
1776 std::list<VirtualSystemDescriptionEntry*> vsdeAudioAdapter = vsdescThis->findByType(VirtualSystemDescriptionType_SoundCard);
1777 /* @todo: we support one audio adapter only */
1778 if (vsdeAudioAdapter.size() > 0)
1779 {
1780 const Utf8Str& audioAdapterVBox = vsdeAudioAdapter.front()->strVbox;
1781 if (audioAdapterVBox.compare("null", Utf8Str::CaseInsensitive) != 0)
1782 {
1783 uint32_t audio = RTStrToUInt32(audioAdapterVBox.c_str());
1784 ComPtr<IAudioAdapter> audioAdapter;
1785 rc = pNewMachine->COMGETTER(AudioAdapter)(audioAdapter.asOutParam());
1786 if (FAILED(rc)) throw rc;
1787 rc = audioAdapter->COMSETTER(Enabled)(true);
1788 if (FAILED(rc)) throw rc;
1789 rc = audioAdapter->COMSETTER(AudioController)(static_cast<AudioControllerType_T>(audio));
1790 if (FAILED(rc)) throw rc;
1791 }
1792 }
1793
1794#ifdef VBOX_WITH_USB
1795 /* USB Controller */
1796 std::list<VirtualSystemDescriptionEntry*> vsdeUSBController = vsdescThis->findByType(VirtualSystemDescriptionType_USBController);
1797 // USB support is enabled if there's at least one such entry; to disable USB support,
1798 // the type of the USB item would have been changed to "ignore"
1799 bool fUSBEnabled = vsdeUSBController.size() > 0;
1800
1801 ComPtr<IUSBController> usbController;
1802 rc = pNewMachine->COMGETTER(USBController)(usbController.asOutParam());
1803 if (FAILED(rc)) throw rc;
1804 rc = usbController->COMSETTER(Enabled)(fUSBEnabled);
1805 if (FAILED(rc)) throw rc;
1806#endif /* VBOX_WITH_USB */
1807
1808 /* Change the network adapters */
1809 std::list<VirtualSystemDescriptionEntry*> vsdeNW = vsdescThis->findByType(VirtualSystemDescriptionType_NetworkAdapter);
1810 if (vsdeNW.size() == 0)
1811 {
1812 /* No network adapters, so we have to disable our default one */
1813 ComPtr<INetworkAdapter> nwVBox;
1814 rc = pNewMachine->GetNetworkAdapter(0, nwVBox.asOutParam());
1815 if (FAILED(rc)) throw rc;
1816 rc = nwVBox->COMSETTER(Enabled)(false);
1817 if (FAILED(rc)) throw rc;
1818 }
1819 else
1820 {
1821 list<VirtualSystemDescriptionEntry*>::const_iterator nwIt;
1822 /* Iterate through all network cards. We support 8 network adapters
1823 * at the maximum. (@todo: warn if there are more!) */
1824 size_t a = 0;
1825 for (nwIt = vsdeNW.begin();
1826 (nwIt != vsdeNW.end() && a < SchemaDefs::NetworkAdapterCount);
1827 ++nwIt, ++a)
1828 {
1829 const VirtualSystemDescriptionEntry* pvsys = *nwIt;
1830
1831 const Utf8Str &nwTypeVBox = pvsys->strVbox;
1832 uint32_t tt1 = RTStrToUInt32(nwTypeVBox.c_str());
1833 ComPtr<INetworkAdapter> pNetworkAdapter;
1834 rc = pNewMachine->GetNetworkAdapter((ULONG)a, pNetworkAdapter.asOutParam());
1835 if (FAILED(rc)) throw rc;
1836 /* Enable the network card & set the adapter type */
1837 rc = pNetworkAdapter->COMSETTER(Enabled)(true);
1838 if (FAILED(rc)) throw rc;
1839 rc = pNetworkAdapter->COMSETTER(AdapterType)(static_cast<NetworkAdapterType_T>(tt1));
1840 if (FAILED(rc)) throw rc;
1841
1842 // default is NAT; change to "bridged" if extra conf says so
1843 if (!pvsys->strExtraConfig.compare("type=Bridged", Utf8Str::CaseInsensitive))
1844 {
1845 /* Attach to the right interface */
1846 rc = pNetworkAdapter->AttachToBridgedInterface();
1847 if (FAILED(rc)) throw rc;
1848 ComPtr<IHost> host;
1849 rc = pVirtualBox->COMGETTER(Host)(host.asOutParam());
1850 if (FAILED(rc)) throw rc;
1851 com::SafeIfaceArray<IHostNetworkInterface> nwInterfaces;
1852 rc = host->COMGETTER(NetworkInterfaces)(ComSafeArrayAsOutParam(nwInterfaces));
1853 if (FAILED(rc)) throw rc;
1854 /* We search for the first host network interface which
1855 * is usable for bridged networking */
1856 for (size_t i=0; i < nwInterfaces.size(); ++i)
1857 {
1858 HostNetworkInterfaceType_T itype;
1859 rc = nwInterfaces[i]->COMGETTER(InterfaceType)(&itype);
1860 if (FAILED(rc)) throw rc;
1861 if (itype == HostNetworkInterfaceType_Bridged)
1862 {
1863 Bstr name;
1864 rc = nwInterfaces[i]->COMGETTER(Name)(name.asOutParam());
1865 if (FAILED(rc)) throw rc;
1866 /* Set the interface name to attach to */
1867 pNetworkAdapter->COMSETTER(HostInterface)(name);
1868 if (FAILED(rc)) throw rc;
1869 break;
1870 }
1871 }
1872 }
1873 /* Next test for host only interfaces */
1874 else if (!pvsys->strExtraConfig.compare("type=HostOnly", Utf8Str::CaseInsensitive))
1875 {
1876 /* Attach to the right interface */
1877 rc = pNetworkAdapter->AttachToHostOnlyInterface();
1878 if (FAILED(rc)) throw rc;
1879 ComPtr<IHost> host;
1880 rc = pVirtualBox->COMGETTER(Host)(host.asOutParam());
1881 if (FAILED(rc)) throw rc;
1882 com::SafeIfaceArray<IHostNetworkInterface> nwInterfaces;
1883 rc = host->COMGETTER(NetworkInterfaces)(ComSafeArrayAsOutParam(nwInterfaces));
1884 if (FAILED(rc)) throw rc;
1885 /* We search for the first host network interface which
1886 * is usable for host only networking */
1887 for (size_t i=0; i < nwInterfaces.size(); ++i)
1888 {
1889 HostNetworkInterfaceType_T itype;
1890 rc = nwInterfaces[i]->COMGETTER(InterfaceType)(&itype);
1891 if (FAILED(rc)) throw rc;
1892 if (itype == HostNetworkInterfaceType_HostOnly)
1893 {
1894 Bstr name;
1895 rc = nwInterfaces[i]->COMGETTER(Name)(name.asOutParam());
1896 if (FAILED(rc)) throw rc;
1897 /* Set the interface name to attach to */
1898 pNetworkAdapter->COMSETTER(HostInterface)(name);
1899 if (FAILED(rc)) throw rc;
1900 break;
1901 }
1902 }
1903 }
1904 }
1905 }
1906
1907 /* Floppy drive */
1908 std::list<VirtualSystemDescriptionEntry*> vsdeFloppy = vsdescThis->findByType(VirtualSystemDescriptionType_Floppy);
1909 // Floppy support is enabled if there's at least one such entry; to disable floppy support,
1910 // the type of the floppy item would have been changed to "ignore"
1911 bool fFloppyEnabled = vsdeFloppy.size() > 0;
1912 ComPtr<IFloppyDrive> floppyDrive;
1913 rc = pNewMachine->COMGETTER(FloppyDrive)(floppyDrive.asOutParam());
1914 if (FAILED(rc)) throw rc;
1915 rc = floppyDrive->COMSETTER(Enabled)(fFloppyEnabled);
1916 if (FAILED(rc)) throw rc;
1917
1918 /* CDROM drive */
1919 /* @todo: I can't disable the CDROM. So nothing to do for now */
1920 // std::list<VirtualSystemDescriptionEntry*> vsdeFloppy = vsd->findByType(VirtualSystemDescriptionType_CDROM);
1921
1922 /* Hard disk controller IDE */
1923 std::list<VirtualSystemDescriptionEntry*> vsdeHDCIDE = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerIDE);
1924 if (vsdeHDCIDE.size() > 1)
1925 throw setError(VBOX_E_FILE_ERROR,
1926 tr("Too many IDE controllers in OVF; VirtualBox only supports one"));
1927 if (vsdeHDCIDE.size() == 1)
1928 {
1929 ComPtr<IStorageController> pController;
1930 rc = pNewMachine->GetStorageControllerByName(Bstr("IDE"), pController.asOutParam());
1931 if (FAILED(rc)) throw rc;
1932
1933 const char *pcszIDEType = vsdeHDCIDE.front()->strVbox.c_str();
1934 if (!strcmp(pcszIDEType, "PIIX3"))
1935 rc = pController->COMSETTER(ControllerType)(StorageControllerType_PIIX3);
1936 else if (!strcmp(pcszIDEType, "PIIX4"))
1937 rc = pController->COMSETTER(ControllerType)(StorageControllerType_PIIX4);
1938 else if (!strcmp(pcszIDEType, "ICH6"))
1939 rc = pController->COMSETTER(ControllerType)(StorageControllerType_ICH6);
1940 else
1941 throw setError(VBOX_E_FILE_ERROR,
1942 tr("Invalid IDE controller type \"%s\""),
1943 pcszIDEType);
1944 if (FAILED(rc)) throw rc;
1945 }
1946#ifdef VBOX_WITH_AHCI
1947 /* Hard disk controller SATA */
1948 std::list<VirtualSystemDescriptionEntry*> vsdeHDCSATA = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerSATA);
1949 if (vsdeHDCSATA.size() > 1)
1950 throw setError(VBOX_E_FILE_ERROR,
1951 tr("Too many SATA controllers in OVF; VirtualBox only supports one"));
1952 if (vsdeHDCSATA.size() > 0)
1953 {
1954 ComPtr<IStorageController> pController;
1955 const Utf8Str &hdcVBox = vsdeHDCSATA.front()->strVbox;
1956 if (hdcVBox == "AHCI")
1957 {
1958 rc = pNewMachine->AddStorageController(Bstr("SATA"), StorageBus_SATA, pController.asOutParam());
1959 if (FAILED(rc)) throw rc;
1960 }
1961 else
1962 throw setError(VBOX_E_FILE_ERROR,
1963 tr("Invalid SATA controller type \"%s\""),
1964 hdcVBox.c_str());
1965 }
1966#endif /* VBOX_WITH_AHCI */
1967
1968 /* Hard disk controller SCSI */
1969 std::list<VirtualSystemDescriptionEntry*> vsdeHDCSCSI = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerSCSI);
1970 if (vsdeHDCSCSI.size() > 1)
1971 throw setError(VBOX_E_FILE_ERROR,
1972 tr("Too many SCSI controllers in OVF; VirtualBox only supports one"));
1973 if (vsdeHDCSCSI.size() > 0)
1974 {
1975 ComPtr<IStorageController> pController;
1976 StorageControllerType_T controllerType;
1977 const Utf8Str &hdcVBox = vsdeHDCSCSI.front()->strVbox;
1978 if (hdcVBox == "LsiLogic")
1979 controllerType = StorageControllerType_LsiLogic;
1980 else if (hdcVBox == "BusLogic")
1981 controllerType = StorageControllerType_BusLogic;
1982 else
1983 throw setError(VBOX_E_FILE_ERROR,
1984 tr("Invalid SCSI controller type \"%s\""),
1985 hdcVBox.c_str());
1986
1987 rc = pNewMachine->AddStorageController(Bstr("SCSI"), StorageBus_SCSI, pController.asOutParam());
1988 if (FAILED(rc)) throw rc;
1989 rc = pController->COMSETTER(ControllerType)(controllerType);
1990 if (FAILED(rc)) throw rc;
1991 }
1992
1993 /* Now its time to register the machine before we add any hard disks */
1994 rc = pVirtualBox->RegisterMachine(pNewMachine);
1995 if (FAILED(rc)) throw rc;
1996
1997 Guid newMachineId;
1998 rc = pNewMachine->COMGETTER(Id)(newMachineId.asOutParam());
1999 if (FAILED(rc)) throw rc;
2000
2001 // store new machine for roll-back in case of errors
2002 llMachinesRegistered.push_back(newMachineId);
2003
2004 /* Create the hard disks & connect them to the appropriate controllers. */
2005 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);
2006 if (avsdeHDs.size() > 0)
2007 {
2008 /* If in the next block an error occur we have to deregister
2009 the machine, so make an extra try/catch block. */
2010 ComPtr<IHardDisk> srcHdVBox;
2011 bool fSourceHdNeedsClosing = false;
2012
2013 try
2014 {
2015 /* In order to attach hard disks we need to open a session
2016 * for the new machine */
2017 rc = pVirtualBox->OpenSession(session, newMachineId);
2018 if (FAILED(rc)) throw rc;
2019 fSessionOpen = true;
2020
2021 /* The disk image has to be on the same place as the OVF file. So
2022 * strip the filename out of the full file path. */
2023 Utf8Str strSrcDir = stripFilename(pAppliance->m->strPath);
2024
2025 /* Iterate over all given disk images */
2026 list<VirtualSystemDescriptionEntry*>::const_iterator itHD;
2027 for (itHD = avsdeHDs.begin();
2028 itHD != avsdeHDs.end();
2029 ++itHD)
2030 {
2031 VirtualSystemDescriptionEntry *vsdeHD = *itHD;
2032
2033 const char *pcszDstFilePath = vsdeHD->strVbox.c_str();
2034 /* Check if the destination file exists already or the
2035 * destination path is empty. */
2036 if ( !(*pcszDstFilePath)
2037 || RTPathExists(pcszDstFilePath)
2038 )
2039 /* This isn't allowed */
2040 throw setError(VBOX_E_FILE_ERROR,
2041 tr("Destination file '%s' exists",
2042 pcszDstFilePath));
2043
2044 /* Find the disk from the OVF's disk list */
2045 DiskImagesMap::const_iterator itDiskImage = pAppliance->m->mapDisks.find(vsdeHD->strRef);
2046 /* vsdeHD->strRef contains the disk identifier (e.g. "vmdisk1"), which should exist
2047 in the virtual system's disks map under that ID and also in the global images map. */
2048 VirtualDisksMap::const_iterator itVirtualDisk = vsysThis.mapVirtualDisks.find(vsdeHD->strRef);
2049
2050 if ( itDiskImage == pAppliance->m->mapDisks.end()
2051 || itVirtualDisk == vsysThis.mapVirtualDisks.end()
2052 )
2053 throw setError(E_FAIL,
2054 tr("Internal inconsistency looking up disk images."));
2055
2056 const DiskImage &di = itDiskImage->second;
2057 const VirtualDisk &vd = itVirtualDisk->second;
2058
2059 /* Make sure all target directories exists */
2060 rc = VirtualBox::ensureFilePathExists(pcszDstFilePath);
2061 if (FAILED(rc))
2062 throw rc;
2063
2064 // subprogress object for hard disk
2065 ComPtr<IProgress> pProgress2;
2066
2067 ComPtr<IHardDisk> dstHdVBox;
2068 /* If strHref is empty we have to create a new file */
2069 if (di.strHref.isEmpty())
2070 {
2071 /* Which format to use? */
2072 Bstr srcFormat = L"VDI";
2073 if ( di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#sparse", Utf8Str::CaseInsensitive)
2074 || di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#compressed", Utf8Str::CaseInsensitive))
2075 srcFormat = L"VMDK";
2076 /* Create an empty hard disk */
2077 rc = pVirtualBox->CreateHardDisk(srcFormat, Bstr(pcszDstFilePath), dstHdVBox.asOutParam());
2078 if (FAILED(rc)) throw rc;
2079
2080 /* Create a dynamic growing disk image with the given capacity */
2081 rc = dstHdVBox->CreateBaseStorage(di.iCapacity / _1M, HardDiskVariant_Standard, pProgress2.asOutParam());
2082 if (FAILED(rc)) throw rc;
2083
2084 /* Advance to the next operation */
2085 if (!task->progress.isNull())
2086 task->progress->setNextOperation(BstrFmt(tr("Creating virtual disk image '%s'"), pcszDstFilePath),
2087 vsdeHD->ulSizeMB); // operation's weight, as set up with the IProgress originally
2088 }
2089 else
2090 {
2091 /* Construct the source file path */
2092 Utf8StrFmt strSrcFilePath("%s%c%s", strSrcDir.c_str(), RTPATH_DELIMITER, di.strHref.c_str());
2093 /* Check if the source file exists */
2094 if (!RTPathExists(strSrcFilePath.c_str()))
2095 /* This isn't allowed */
2096 throw setError(VBOX_E_FILE_ERROR,
2097 tr("Source virtual disk image file '%s' doesn't exist"),
2098 strSrcFilePath.c_str());
2099
2100 /* Clone the disk image (this is necessary cause the id has
2101 * to be recreated for the case the same hard disk is
2102 * attached already from a previous import) */
2103
2104 /* First open the existing disk image */
2105 rc = pVirtualBox->OpenHardDisk(Bstr(strSrcFilePath),
2106 AccessMode_ReadOnly,
2107 srcHdVBox.asOutParam());
2108 if (FAILED(rc)) throw rc;
2109 fSourceHdNeedsClosing = true;
2110
2111 /* We need the format description of the source disk image */
2112 Bstr srcFormat;
2113 rc = srcHdVBox->COMGETTER(Format)(srcFormat.asOutParam());
2114 if (FAILED(rc)) throw rc;
2115 /* Create a new hard disk interface for the destination disk image */
2116 rc = pVirtualBox->CreateHardDisk(srcFormat, Bstr(pcszDstFilePath), dstHdVBox.asOutParam());
2117 if (FAILED(rc)) throw rc;
2118 /* Clone the source disk image */
2119 rc = srcHdVBox->CloneTo(dstHdVBox, HardDiskVariant_Standard, pProgress2.asOutParam());
2120 if (FAILED(rc)) throw rc;
2121
2122 /* Advance to the next operation */
2123 if (!task->progress.isNull())
2124 task->progress->setNextOperation(BstrFmt(tr("Importing virtual disk image '%s'"), strSrcFilePath.c_str()),
2125 vsdeHD->ulSizeMB); // operation's weight, as set up with the IProgress originally);
2126 }
2127
2128 // now wait for the background disk operation to complete; this throws HRESULTs on error
2129 pAppliance->waitForAsyncProgress(task->progress, pProgress2);
2130
2131 if (fSourceHdNeedsClosing)
2132 {
2133 rc = srcHdVBox->Close();
2134 if (FAILED(rc)) throw rc;
2135 fSourceHdNeedsClosing = false;
2136 }
2137
2138 llHardDisksCreated.push_back(dstHdVBox);
2139 /* Now use the new uuid to attach the disk image to our new machine */
2140 ComPtr<IMachine> sMachine;
2141 rc = session->COMGETTER(Machine)(sMachine.asOutParam());
2142 if (FAILED(rc)) throw rc;
2143 Guid hdId;
2144 rc = dstHdVBox->COMGETTER(Id)(hdId.asOutParam());
2145 if (FAILED(rc)) throw rc;
2146
2147 /* For now we assume we have one controller of every type only */
2148 HardDiskController hdc = (*vsysThis.mapControllers.find(vd.idController)).second;
2149
2150 // this is for rollback later
2151 MyHardDiskAttachment mhda;
2152 mhda.uuid = newMachineId;
2153 mhda.pMachine = pNewMachine;
2154
2155 switch (hdc.system)
2156 {
2157 case HardDiskController::IDE:
2158 // For the IDE bus, the channel parameter can be either 0 or 1, to specify the primary
2159 // or secondary IDE controller, respectively. For the primary controller of the IDE bus,
2160 // the device number can be either 0 or 1, to specify the master or the slave device,
2161 // respectively. For the secondary IDE controller, the device number is always 1 because
2162 // the master device is reserved for the CD-ROM drive.
2163 mhda.controllerType = Bstr("IDE");
2164 switch (vd.ulAddressOnParent)
2165 {
2166 case 0: // interpret this as primary master
2167 mhda.lChannel = (long)0;
2168 mhda.lDevice = (long)0;
2169 break;
2170
2171 case 1: // interpret this as primary slave
2172 mhda.lChannel = (long)0;
2173 mhda.lDevice = (long)1;
2174 break;
2175
2176 case 2: // interpret this as secondary slave
2177 mhda.lChannel = (long)1;
2178 mhda.lDevice = (long)1;
2179 break;
2180
2181 default:
2182 throw setError(VBOX_E_NOT_SUPPORTED,
2183 tr("Invalid channel %RI16 specified; IDE controllers support only 0, 1 or 2"), vd.ulAddressOnParent);
2184 break;
2185 }
2186 break;
2187
2188 case HardDiskController::SATA:
2189 mhda.controllerType = Bstr("SATA");
2190 mhda.lChannel = (long)vd.ulAddressOnParent;
2191 mhda.lDevice = (long)0;
2192 break;
2193
2194 case HardDiskController::SCSI:
2195 mhda.controllerType = Bstr("SCSI");
2196 mhda.lChannel = (long)vd.ulAddressOnParent;
2197 mhda.lDevice = (long)0;
2198 break;
2199
2200 default: break;
2201 }
2202
2203 Log(("Attaching disk %s to channel %d on device %d\n", pcszDstFilePath, mhda.lChannel, mhda.lDevice));
2204
2205 rc = sMachine->AttachHardDisk(hdId,
2206 mhda.controllerType,
2207 mhda.lChannel,
2208 mhda.lDevice);
2209 if (FAILED(rc)) throw rc;
2210
2211 llHardDiskAttachments.push_back(mhda);
2212
2213 rc = sMachine->SaveSettings();
2214 if (FAILED(rc)) throw rc;
2215 } // end for (itHD = avsdeHDs.begin();
2216
2217 // only now that we're done with all disks, close the session
2218 rc = session->Close();
2219 if (FAILED(rc)) throw rc;
2220 fSessionOpen = false;
2221 }
2222 catch(HRESULT /* aRC */)
2223 {
2224 if (fSourceHdNeedsClosing)
2225 srcHdVBox->Close();
2226
2227 if (fSessionOpen)
2228 session->Close();
2229
2230 throw;
2231 }
2232 }
2233 }
2234 catch(HRESULT aRC)
2235 {
2236 rc = aRC;
2237 }
2238
2239 if (FAILED(rc))
2240 break;
2241
2242 } // for (it = pAppliance->m->llVirtualSystems.begin(),
2243
2244 if (FAILED(rc))
2245 {
2246 // with _whatever_ error we've had, do a complete roll-back of
2247 // machines and disks we've created; unfortunately this is
2248 // not so trivially done...
2249
2250 HRESULT rc2;
2251 // detach all hard disks from all machines we created
2252 list<MyHardDiskAttachment>::iterator itM;
2253 for (itM = llHardDiskAttachments.begin();
2254 itM != llHardDiskAttachments.end();
2255 ++itM)
2256 {
2257 const MyHardDiskAttachment &mhda = *itM;
2258 rc2 = pVirtualBox->OpenSession(session, mhda.uuid);
2259 if (SUCCEEDED(rc2))
2260 {
2261 ComPtr<IMachine> sMachine;
2262 rc2 = session->COMGETTER(Machine)(sMachine.asOutParam());
2263 if (SUCCEEDED(rc2))
2264 {
2265 rc2 = sMachine->DetachHardDisk(Bstr(mhda.controllerType), mhda.lChannel, mhda.lDevice);
2266 rc2 = sMachine->SaveSettings();
2267 }
2268 session->Close();
2269 }
2270 }
2271
2272 // now clean up all hard disks we created
2273 list< ComPtr<IHardDisk> >::iterator itHD;
2274 for (itHD = llHardDisksCreated.begin();
2275 itHD != llHardDisksCreated.end();
2276 ++itHD)
2277 {
2278 ComPtr<IHardDisk> pDisk = *itHD;
2279 ComPtr<IProgress> pProgress;
2280 rc2 = pDisk->DeleteStorage(pProgress.asOutParam());
2281 rc2 = pProgress->WaitForCompletion(-1);
2282 }
2283
2284 // finally, deregister and remove all machines
2285 list<Guid>::iterator itID;
2286 for (itID = llMachinesRegistered.begin();
2287 itID != llMachinesRegistered.end();
2288 ++itID)
2289 {
2290 const Guid &guid = *itID;
2291 ComPtr<IMachine> failedMachine;
2292 rc2 = pVirtualBox->UnregisterMachine(guid, failedMachine.asOutParam());
2293 if (SUCCEEDED(rc2))
2294 rc2 = failedMachine->DeleteSettings();
2295 }
2296 }
2297
2298 task->rc = rc;
2299
2300 if (!task->progress.isNull())
2301 task->progress->notifyComplete(rc);
2302
2303 LogFlowFunc(("rc=%Rhrc\n", rc));
2304 LogFlowFuncLeave();
2305
2306 return VINF_SUCCESS;
2307}
2308
2309struct Appliance::TaskWriteOVF
2310{
2311 TaskWriteOVF(Appliance *aThat, Progress *aProgress)
2312 : pAppliance(aThat)
2313 , progress(aProgress)
2314 , rc(S_OK)
2315 {}
2316 ~TaskWriteOVF() {}
2317
2318 HRESULT startThread();
2319
2320 Appliance *pAppliance;
2321 ComObjPtr<Progress> progress;
2322 HRESULT rc;
2323};
2324
2325HRESULT Appliance::TaskWriteOVF::startThread()
2326{
2327 int vrc = RTThreadCreate(NULL, Appliance::taskThreadWriteOVF, this,
2328 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
2329 "Appliance::Task");
2330 ComAssertMsgRCRet(vrc,
2331 ("Could not create taskThreadExportOVF (%Rrc)\n", vrc), E_FAIL);
2332
2333 return S_OK;
2334}
2335
2336STDMETHODIMP Appliance::Write(IN_BSTR path, IProgress **aProgress)
2337{
2338 HRESULT rc = S_OK;
2339
2340 CheckComArgOutPointerValid(aProgress);
2341
2342 AutoCaller autoCaller(this);
2343 if (FAILED(rc = autoCaller.rc())) return rc;
2344
2345 AutoWriteLock(this);
2346
2347 // see if we can handle this file; for now we insist it has an ".ovf" extension
2348 m->strPath = path;
2349 if (!m->strPath.endsWith(".ovf", Utf8Str::CaseInsensitive))
2350 return setError(VBOX_E_FILE_ERROR,
2351 tr("Appliance file must have .ovf extension"));
2352
2353 ComObjPtr<Progress> progress;
2354 try
2355 {
2356 Bstr progressDesc = BstrFmt(tr("Export appliance '%s'"),
2357 m->strPath.raw());
2358 rc = setUpProgress(progress, progressDesc);
2359 if (FAILED(rc)) throw rc;
2360
2361 /* Initialize our worker task */
2362 std::auto_ptr<TaskWriteOVF> task(new TaskWriteOVF(this, progress));
2363 //AssertComRCThrowRC (task->autoCaller.rc());
2364
2365 rc = task->startThread();
2366 CheckComRCThrowRC(rc);
2367
2368 task.release();
2369 }
2370 catch (HRESULT aRC)
2371 {
2372 rc = aRC;
2373 }
2374
2375 if (SUCCEEDED(rc))
2376 /* Return progress to the caller */
2377 progress.queryInterfaceTo(aProgress);
2378
2379 return rc;
2380}
2381
2382/**
2383 * Worker thread implementation for Write() (ovf writer).
2384 * @param aThread
2385 * @param pvUser
2386 */
2387/* static */
2388DECLCALLBACK(int) Appliance::taskThreadWriteOVF(RTTHREAD /* aThread */, void *pvUser)
2389{
2390 std::auto_ptr<TaskWriteOVF> task(static_cast<TaskWriteOVF*>(pvUser));
2391 AssertReturn(task.get(), VERR_GENERAL_FAILURE);
2392
2393 Appliance *pAppliance = task->pAppliance;
2394
2395 LogFlowFuncEnter();
2396 LogFlowFunc(("Appliance %p\n", pAppliance));
2397
2398 AutoCaller autoCaller(pAppliance);
2399 CheckComRCReturnRC(autoCaller.rc());
2400
2401 AutoWriteLock appLock(pAppliance);
2402
2403 HRESULT rc = S_OK;
2404
2405 ComPtr<IVirtualBox> pVirtualBox(pAppliance->mVirtualBox);
2406
2407 try
2408 {
2409 xml::Document doc;
2410 xml::ElementNode *pelmRoot = doc.createRootElement("Envelope");
2411
2412 pelmRoot->setAttribute("ovf:version", "1.0");
2413 pelmRoot->setAttribute("xml:lang", "en-US");
2414 pelmRoot->setAttribute("xmlns", "http://schemas.dmtf.org/ovf/envelope/1");
2415 pelmRoot->setAttribute("xmlns:ovf", "http://schemas.dmtf.org/ovf/envelope/1");
2416 pelmRoot->setAttribute("xmlns:ovfstr", "http://schema.dmtf.org/ovf/strings/1");
2417 pelmRoot->setAttribute("xmlns:rasd", "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData");
2418 pelmRoot->setAttribute("xmlns:vssd", "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData");
2419 pelmRoot->setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
2420 pelmRoot->setAttribute("xsi:schemaLocation", "http://schemas.dmtf.org/ovf/envelope/1 ../ovf-envelope.xsd");
2421
2422 // <Envelope>/<References>
2423 xml::ElementNode *pelmReferences = pelmRoot->createChild("References");
2424
2425 /* <Envelope>/<DiskSection>:
2426 <DiskSection>
2427 <Info>List of the virtual disks used in the package</Info>
2428 <Disk ovf:capacity="4294967296" ovf:diskId="lamp" ovf:format="http://www.vmware.com/specifications/vmdk.html#compressed" ovf:populatedSize="1924967692"/>
2429 </DiskSection> */
2430 xml::ElementNode *pelmDiskSection = pelmRoot->createChild("DiskSection");
2431 xml::ElementNode *pelmDiskSectionInfo = pelmDiskSection->createChild("Info");
2432 pelmDiskSectionInfo->addContent("List of the virtual disks used in the package");
2433 // for now, set up a map so we have a list of unique disk names (to make
2434 // sure the same disk name is only added once)
2435 map<Utf8Str, const VirtualSystemDescriptionEntry*> mapDisks;
2436
2437 /* <Envelope>/<NetworkSection>:
2438 <NetworkSection>
2439 <Info>Logical networks used in the package</Info>
2440 <Network ovf:name="VM Network">
2441 <Description>The network that the LAMP Service will be available on</Description>
2442 </Network>
2443 </NetworkSection> */
2444 xml::ElementNode *pelmNetworkSection = pelmRoot->createChild("NetworkSection");
2445 xml::ElementNode *pelmNetworkSectionInfo = pelmNetworkSection->createChild("Info");
2446 pelmNetworkSectionInfo->addContent("Logical networks used in the package");
2447 // for now, set up a map so we have a list of unique network names (to make
2448 // sure the same network name is only added once)
2449 map<Utf8Str, bool> mapNetworks;
2450 // we fill this later below when we iterate over the networks
2451
2452 // and here come the virtual systems:
2453 xml::ElementNode *pelmVirtualSystemCollection = pelmRoot->createChild("VirtualSystemCollection");
2454 /* xml::AttributeNode *pattrVirtualSystemCollectionId = */ pelmVirtualSystemCollection->setAttribute("ovf:id", "ExportedVirtualBoxMachines"); // whatever
2455
2456 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;
2457 /* Iterate through all virtual systems of that appliance */
2458 for (it = pAppliance->m->virtualSystemDescriptions.begin();
2459 it != pAppliance->m->virtualSystemDescriptions.end();
2460 ++it)
2461 {
2462 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);
2463
2464 xml::ElementNode *pelmVirtualSystem = pelmVirtualSystemCollection->createChild("VirtualSystem");
2465
2466 /*xml::ElementNode *pelmVirtualSystemInfo =*/ pelmVirtualSystem->createChild("Info")->addContent("A virtual machine");
2467
2468 std::list<VirtualSystemDescriptionEntry*> llName = vsdescThis->findByType(VirtualSystemDescriptionType_Name);
2469 if (llName.size() != 1)
2470 throw setError(VBOX_E_NOT_SUPPORTED,
2471 tr("Missing VM name"));
2472 pelmVirtualSystem->setAttribute("ovf:id", llName.front()->strVbox);
2473
2474 // description
2475 std::list<VirtualSystemDescriptionEntry*> llDescription = vsdescThis->findByType(VirtualSystemDescriptionType_Description);
2476 if (llDescription.size())
2477 {
2478 /* <Section ovf:required="false" xsi:type="ovf:AnnotationSection_Type">
2479 <Info>A human-readable annotation</Info>
2480 <Annotation>Plan 9</Annotation>
2481 </Section> */
2482 xml::ElementNode *pelmAnnotationSection = pelmVirtualSystem->createChild("AnnotationSection");
2483 pelmAnnotationSection->createChild("Info")->addContent("A human-readable annotation");
2484 pelmAnnotationSection->createChild("Annotation")->addContent(llDescription.front()->strVbox);
2485 }
2486
2487 // license
2488 std::list<VirtualSystemDescriptionEntry*> llLicense = vsdescThis->findByType(VirtualSystemDescriptionType_License);
2489 if (llLicense.size())
2490 {
2491 /* <EulaSection>
2492 <Info ovf:msgid="6">License agreement for the Virtual System.</Info>
2493 <License ovf:msgid="1">License terms can go in here.</License>
2494 </EulaSection> */
2495 xml::ElementNode *pelmAnnotationSection = pelmVirtualSystem->createChild("EulaSection");
2496 pelmAnnotationSection->createChild("Info")->addContent("License agreement for the Virtual System.");
2497 pelmAnnotationSection->createChild("License")->addContent(llLicense.front()->strVbox);
2498 }
2499
2500 // operating system
2501 std::list<VirtualSystemDescriptionEntry*> llOS = vsdescThis->findByType(VirtualSystemDescriptionType_OS);
2502 if (llOS.size() != 1)
2503 throw setError(VBOX_E_NOT_SUPPORTED,
2504 tr("Missing OS type"));
2505 /* <OperatingSystemSection ovf:id="82">
2506 <Info>Guest Operating System</Info>
2507 <Description>Linux 2.6.x</Description>
2508 </OperatingSystemSection> */
2509 xml::ElementNode *pelmOperatingSystemSection = pelmVirtualSystem->createChild("OperatingSystemSection");
2510 pelmOperatingSystemSection->setAttribute("ovf:id", llOS.front()->strOvf);
2511// pelmOperatingSystemSection->createChild("Info")->addContent("blah"); // @todo
2512// pelmOperatingSystemSection->createChild("Description")->addContent("blah"); // @todo
2513
2514 // <VirtualHardwareSection ovf:id="hw1" ovf:transport="iso">
2515 xml::ElementNode *pelmVirtualHardwareSection = pelmVirtualSystem->createChild("VirtualHardwareSection");
2516
2517 /* <System>
2518 <vssd:Description>Description of the virtual hardware section.</vssd:Description>
2519 <vssd:ElementName>vmware</vssd:ElementName>
2520 <vssd:InstanceID>1</vssd:InstanceID>
2521 <vssd:VirtualSystemIdentifier>MyLampService</vssd:VirtualSystemIdentifier>
2522 <vssd:VirtualSystemType>vmx-4</vssd:VirtualSystemType>
2523 </System> */
2524 xml::ElementNode *pelmSystem = pelmVirtualHardwareSection->createChild("System");
2525
2526 // <vssd:VirtualSystemType>vmx-4</vssd:VirtualSystemType>
2527 xml::ElementNode *pelmVirtualSystemType = pelmSystem->createChild("VirtualSystemType");
2528 pelmVirtualSystemType->addContent("virtualbox-2.2"); // instead of vmx-7?
2529
2530 // loop thru all description entries twice; once to write out all
2531 // devices _except_ disk images, and a second time to assign the
2532 // disk images; this is because disk images need to reference
2533 // IDE controllers, and we can't know their instance IDs without
2534 // assigning them first
2535
2536 uint32_t idIDEController = 0;
2537 int32_t lIDEControllerIndex = 0;
2538 uint32_t idSATAController = 0;
2539 int32_t lSATAControllerIndex = 0;
2540 uint32_t idSCSIController = 0;
2541 int32_t lSCSIControllerIndex = 0;
2542
2543 uint32_t ulInstanceID = 1;
2544 uint32_t cDisks = 0;
2545
2546 for (size_t uLoop = 1;
2547 uLoop <= 2;
2548 ++uLoop)
2549 {
2550 int32_t lIndexThis = 0;
2551 list<VirtualSystemDescriptionEntry>::const_iterator itD;
2552 for (itD = vsdescThis->m->llDescriptions.begin();
2553 itD != vsdescThis->m->llDescriptions.end();
2554 ++itD, ++lIndexThis)
2555 {
2556 const VirtualSystemDescriptionEntry &desc = *itD;
2557
2558 OVFResourceType_T type = (OVFResourceType_T)0; // if this becomes != 0 then we do stuff
2559 Utf8Str strResourceSubType;
2560
2561 Utf8Str strDescription; // results in <rasd:Description>...</rasd:Description> block
2562 Utf8Str strCaption; // results in <rasd:Caption>...</rasd:Caption> block
2563
2564 uint32_t ulParent = 0;
2565
2566 int32_t lVirtualQuantity = -1;
2567 Utf8Str strAllocationUnits;
2568
2569 int32_t lAddress = -1;
2570 int32_t lBusNumber = -1;
2571 int32_t lAddressOnParent = -1;
2572
2573 int32_t lAutomaticAllocation = -1; // 0 means "false", 1 means "true"
2574 Utf8Str strConnection; // results in <rasd:Connection>...</rasd:Connection> block
2575 Utf8Str strHostResource;
2576
2577 uint64_t uTemp;
2578
2579 switch (desc.type)
2580 {
2581 case VirtualSystemDescriptionType_CPU:
2582 /* <Item>
2583 <rasd:Caption>1 virtual CPU</rasd:Caption>
2584 <rasd:Description>Number of virtual CPUs</rasd:Description>
2585 <rasd:ElementName>virtual CPU</rasd:ElementName>
2586 <rasd:InstanceID>1</rasd:InstanceID>
2587 <rasd:ResourceType>3</rasd:ResourceType>
2588 <rasd:VirtualQuantity>1</rasd:VirtualQuantity>
2589 </Item> */
2590 if (uLoop == 1)
2591 {
2592 strDescription = "Number of virtual CPUs";
2593 type = OVFResourceType_Processor; // 3
2594 lVirtualQuantity = 1;
2595 }
2596 break;
2597
2598 case VirtualSystemDescriptionType_Memory:
2599 /* <Item>
2600 <rasd:AllocationUnits>MegaBytes</rasd:AllocationUnits>
2601 <rasd:Caption>256 MB of memory</rasd:Caption>
2602 <rasd:Description>Memory Size</rasd:Description>
2603 <rasd:ElementName>Memory</rasd:ElementName>
2604 <rasd:InstanceID>2</rasd:InstanceID>
2605 <rasd:ResourceType>4</rasd:ResourceType>
2606 <rasd:VirtualQuantity>256</rasd:VirtualQuantity>
2607 </Item> */
2608 if (uLoop == 1)
2609 {
2610 strDescription = "Memory Size";
2611 type = OVFResourceType_Memory; // 4
2612 desc.strVbox.toInt(uTemp);
2613 lVirtualQuantity = (int32_t)(uTemp / _1M);
2614 strAllocationUnits = "MegaBytes";
2615 }
2616 break;
2617
2618 case VirtualSystemDescriptionType_HardDiskControllerIDE:
2619 /* <Item>
2620 <rasd:Caption>ideController1</rasd:Caption>
2621 <rasd:Description>IDE Controller</rasd:Description>
2622 <rasd:InstanceId>5</rasd:InstanceId>
2623 <rasd:ResourceType>5</rasd:ResourceType>
2624 <rasd:Address>1</rasd:Address>
2625 <rasd:BusNumber>1</rasd:BusNumber>
2626 </Item> */
2627 if (uLoop == 1)
2628 {
2629 strDescription = "IDE Controller";
2630 type = OVFResourceType_IDEController; // 5
2631 // it seems that OVFTool always writes these two, and since we can only
2632 // have one IDE controller, we'll use this as well
2633 lAddress = 1;
2634 lBusNumber = 1;
2635
2636 // remember this ID
2637 idIDEController = ulInstanceID;
2638 lIDEControllerIndex = lIndexThis;
2639 }
2640 break;
2641
2642 case VirtualSystemDescriptionType_HardDiskControllerSATA:
2643 /* <Item>
2644 <rasd:Caption>sataController0</rasd:Caption>
2645 <rasd:Description>SATA Controller</rasd:Description>
2646 <rasd:InstanceId>4</rasd:InstanceId>
2647 <rasd:ResourceType>20</rasd:ResourceType>
2648 <rasd:ResourceSubType>ahci</rasd:ResourceSubType>
2649 <rasd:Address>0</rasd:Address>
2650 <rasd:BusNumber>0</rasd:BusNumber>
2651 </Item>
2652 */
2653 if (uLoop == 1)
2654 {
2655 strDescription = "SATA Controller";
2656 strCaption = "sataController0";
2657 type = OVFResourceType_OtherStorageDevice; // 20
2658 // it seems that OVFTool always writes these two, and since we can only
2659 // have one SATA controller, we'll use this as well
2660 lAddress = 0;
2661 lBusNumber = 0;
2662
2663 if ( desc.strVbox.isEmpty() // AHCI is the default in VirtualBox
2664 || (!desc.strVbox.compare("ahci", Utf8Str::CaseInsensitive))
2665 )
2666 strResourceSubType = "AHCI";
2667 else
2668 throw setError(VBOX_E_NOT_SUPPORTED,
2669 tr("Invalid config string \"%s\" in SATA controller"), desc.strVbox.c_str());
2670
2671 // remember this ID
2672 idSATAController = ulInstanceID;
2673 lSATAControllerIndex = lIndexThis;
2674 }
2675 break;
2676
2677 case VirtualSystemDescriptionType_HardDiskControllerSCSI:
2678 /* <Item>
2679 <rasd:Caption>scsiController0</rasd:Caption>
2680 <rasd:Description>SCSI Controller</rasd:Description>
2681 <rasd:InstanceId>4</rasd:InstanceId>
2682 <rasd:ResourceType>6</rasd:ResourceType>
2683 <rasd:ResourceSubType>buslogic</rasd:ResourceSubType>
2684 <rasd:Address>0</rasd:Address>
2685 <rasd:BusNumber>0</rasd:BusNumber>
2686 </Item>
2687 */
2688 if (uLoop == 1)
2689 {
2690 strDescription = "SCSI Controller";
2691 strCaption = "scsiController0";
2692 type = OVFResourceType_ParallelSCSIHBA; // 6
2693 // it seems that OVFTool always writes these two, and since we can only
2694 // have one SATA controller, we'll use this as well
2695 lAddress = 0;
2696 lBusNumber = 0;
2697
2698 if ( desc.strVbox.isEmpty() // LsiLogic is the default in VirtualBox
2699 || (!desc.strVbox.compare("lsilogic", Utf8Str::CaseInsensitive))
2700 )
2701 strResourceSubType = "lsilogic";
2702 else if (!desc.strVbox.compare("buslogic", Utf8Str::CaseInsensitive))
2703 strResourceSubType = "buslogic";
2704 else
2705 throw setError(VBOX_E_NOT_SUPPORTED,
2706 tr("Invalid config string \"%s\" in SCSI controller"), desc.strVbox.c_str());
2707
2708 // remember this ID
2709 idSCSIController = ulInstanceID;
2710 lSCSIControllerIndex = lIndexThis;
2711 }
2712 break;
2713
2714 case VirtualSystemDescriptionType_HardDiskImage:
2715 /* <Item>
2716 <rasd:Caption>disk1</rasd:Caption>
2717 <rasd:InstanceId>8</rasd:InstanceId>
2718 <rasd:ResourceType>17</rasd:ResourceType>
2719 <rasd:HostResource>/disk/vmdisk1</rasd:HostResource>
2720 <rasd:Parent>4</rasd:Parent>
2721 <rasd:AddressOnParent>0</rasd:AddressOnParent>
2722 </Item> */
2723 if (uLoop == 2)
2724 {
2725 Utf8Str strDiskID = Utf8StrFmt("vmdisk%RI32", ++cDisks);
2726
2727 strDescription = "Disk Image";
2728 strCaption = Utf8StrFmt("disk%RI32", cDisks); // this is not used for anything else
2729 type = OVFResourceType_HardDisk; // 17
2730
2731 // the following references the "<Disks>" XML block
2732 strHostResource = Utf8StrFmt("/disk/%s", strDiskID.c_str());
2733
2734 // controller=<index>;channel=<c>
2735 size_t pos1 = desc.strExtraConfig.find("controller=");
2736 size_t pos2 = desc.strExtraConfig.find("channel=");
2737 if (pos1 != Utf8Str::npos)
2738 {
2739 int32_t lControllerIndex = -1;
2740 RTStrToInt32Ex(desc.strExtraConfig.c_str() + pos1 + 11, NULL, 0, &lControllerIndex);
2741 if (lControllerIndex == lIDEControllerIndex)
2742 ulParent = idIDEController;
2743 else if (lControllerIndex == lSCSIControllerIndex)
2744 ulParent = idSCSIController;
2745 else if (lControllerIndex == lSATAControllerIndex)
2746 ulParent = idSATAController;
2747 }
2748 if (pos2 != Utf8Str::npos)
2749 RTStrToInt32Ex(desc.strExtraConfig.c_str() + pos2 + 8, NULL, 0, &lAddressOnParent);
2750
2751 if ( !ulParent
2752 || lAddressOnParent == -1
2753 )
2754 throw setError(VBOX_E_NOT_SUPPORTED,
2755 tr("Missing or bad extra config string in hard disk image: \"%s\""), desc.strExtraConfig.c_str());
2756
2757 mapDisks[strDiskID] = &desc;
2758 }
2759 break;
2760
2761 case VirtualSystemDescriptionType_Floppy:
2762 if (uLoop == 1)
2763 {
2764 strDescription = "Floppy Drive";
2765 strCaption = "floppy0"; // this is what OVFTool writes
2766 type = OVFResourceType_FloppyDrive; // 14
2767 lAutomaticAllocation = 0;
2768 lAddressOnParent = 0; // this is what OVFTool writes
2769 }
2770 break;
2771
2772 case VirtualSystemDescriptionType_CDROM:
2773 if (uLoop == 2)
2774 {
2775 // we can't have a CD without an IDE controller
2776 if (!idIDEController)
2777 throw setError(VBOX_E_NOT_SUPPORTED,
2778 tr("Can't have CD-ROM without IDE controller"));
2779
2780 strDescription = "CD-ROM Drive";
2781 strCaption = "cdrom1"; // this is what OVFTool writes
2782 type = OVFResourceType_CDDrive; // 15
2783 lAutomaticAllocation = 1;
2784 ulParent = idIDEController;
2785 lAddressOnParent = 0; // this is what OVFTool writes
2786 }
2787 break;
2788
2789 case VirtualSystemDescriptionType_NetworkAdapter:
2790 /* <Item>
2791 <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
2792 <rasd:Caption>Ethernet adapter on 'VM Network'</rasd:Caption>
2793 <rasd:Connection>VM Network</rasd:Connection>
2794 <rasd:ElementName>VM network</rasd:ElementName>
2795 <rasd:InstanceID>3</rasd:InstanceID>
2796 <rasd:ResourceType>10</rasd:ResourceType>
2797 </Item> */
2798 if (uLoop == 1)
2799 {
2800 lAutomaticAllocation = 1;
2801 strCaption = Utf8StrFmt("Ethernet adapter on '%s'", desc.strOvf.c_str());
2802 type = OVFResourceType_EthernetAdapter; // 10
2803 /* Set the hardware type to something useful.
2804 * To be compatible with vmware & others we set
2805 * PCNet32 for our PCNet types & E1000 for the
2806 * E1000 cards. */
2807 switch (desc.strVbox.toInt32())
2808 {
2809 case NetworkAdapterType_Am79C970A:
2810 case NetworkAdapterType_Am79C973: strResourceSubType = "PCNet32"; break;
2811#ifdef VBOX_WITH_E1000
2812 case NetworkAdapterType_I82540EM:
2813 case NetworkAdapterType_I82543GC: strResourceSubType = "E1000"; break;
2814#endif /* VBOX_WITH_E1000 */
2815 }
2816 strConnection = desc.strOvf;
2817
2818 mapNetworks[desc.strOvf] = true;
2819 }
2820 break;
2821
2822 case VirtualSystemDescriptionType_USBController:
2823 /* <Item ovf:required="false">
2824 <rasd:Caption>usb</rasd:Caption>
2825 <rasd:Description>USB Controller</rasd:Description>
2826 <rasd:InstanceId>3</rasd:InstanceId>
2827 <rasd:ResourceType>23</rasd:ResourceType>
2828 <rasd:Address>0</rasd:Address>
2829 <rasd:BusNumber>0</rasd:BusNumber>
2830 </Item> */
2831 if (uLoop == 1)
2832 {
2833 strDescription = "USB Controller";
2834 strCaption = "usb";
2835 type = OVFResourceType_USBController; // 23
2836 lAddress = 0; // this is what OVFTool writes
2837 lBusNumber = 0; // this is what OVFTool writes
2838 }
2839 break;
2840
2841 case VirtualSystemDescriptionType_SoundCard:
2842 /* <Item ovf:required="false">
2843 <rasd:Caption>sound</rasd:Caption>
2844 <rasd:Description>Sound Card</rasd:Description>
2845 <rasd:InstanceId>10</rasd:InstanceId>
2846 <rasd:ResourceType>35</rasd:ResourceType>
2847 <rasd:ResourceSubType>ensoniq1371</rasd:ResourceSubType>
2848 <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
2849 <rasd:AddressOnParent>3</rasd:AddressOnParent>
2850 </Item> */
2851 if (uLoop == 1)
2852 {
2853 strDescription = "Sound Card";
2854 strCaption = "sound";
2855 type = OVFResourceType_SoundCard; // 35
2856 strResourceSubType = desc.strOvf; // e.g. ensoniq1371
2857 lAutomaticAllocation = 0;
2858 lAddressOnParent = 3; // what gives? this is what OVFTool writes
2859 }
2860 break;
2861 }
2862
2863 if (type)
2864 {
2865 xml::ElementNode *pItem;
2866
2867 pItem = pelmVirtualHardwareSection->createChild("Item");
2868
2869 if (!strDescription.isEmpty())
2870 pItem->createChild("rasd:Description")->addContent(strDescription);
2871 if (!strCaption.isEmpty())
2872 pItem->createChild("rasd:Caption")->addContent(strCaption);
2873
2874 if (!strAllocationUnits.isEmpty())
2875 pItem->createChild("rasd:AllocationUnits")->addContent(strAllocationUnits);
2876
2877 if (lAutomaticAllocation != -1)
2878 pItem->createChild("rasd:AutomaticAllocation")->addContent( (lAutomaticAllocation) ? "true" : "false" );
2879
2880 if (!strConnection.isEmpty())
2881 pItem->createChild("rasd:Connection")->addContent(strConnection);
2882
2883 // <rasd:InstanceID>1</rasd:InstanceID>
2884 pItem->createChild("rasd:InstanceID")->addContent(Utf8StrFmt("%d", ulInstanceID));
2885 ++ulInstanceID;
2886
2887 // <rasd:ResourceType>3</rasd:ResourceType>
2888 pItem->createChild("rasd:ResourceType")->addContent(Utf8StrFmt("%d", type));
2889 if (!strResourceSubType.isEmpty())
2890 pItem->createChild("rasd:ResourceSubType")->addContent(strResourceSubType);
2891
2892 // <rasd:VirtualQuantity>1</rasd:VirtualQuantity>
2893 if (lVirtualQuantity != -1)
2894 pItem->createChild("rasd:VirtualQuantity")->addContent(Utf8StrFmt("%d", lVirtualQuantity));
2895
2896 if (lAddress != -1)
2897 pItem->createChild("rasd:Address")->addContent(Utf8StrFmt("%d", lAddress));
2898
2899 if (lBusNumber != -1)
2900 pItem->createChild("rasd:BusNumber")->addContent(Utf8StrFmt("%d", lBusNumber));
2901
2902 if (ulParent)
2903 pItem->createChild("rasd:Parent")->addContent(Utf8StrFmt("%d", ulParent));
2904 if (lAddressOnParent != -1)
2905 pItem->createChild("rasd:AddressOnParent")->addContent(Utf8StrFmt("%d", lAddressOnParent));
2906
2907 if (!strHostResource.isEmpty())
2908 pItem->createChild("rasd:HostResource")->addContent(strHostResource);
2909 }
2910 }
2911 } // for (size_t uLoop = 0; ...
2912 }
2913
2914 // finally, fill in the network section we set up empty above according
2915 // to the networks we found with the hardware items
2916 map<Utf8Str, bool>::const_iterator itN;
2917 for (itN = mapNetworks.begin();
2918 itN != mapNetworks.end();
2919 ++itN)
2920 {
2921 const Utf8Str &strNetwork = itN->first;
2922 xml::ElementNode *pelmNetwork = pelmNetworkSection->createChild("Network");
2923 pelmNetwork->setAttribute("ovf:name", strNetwork.c_str());
2924 pelmNetwork->createChild("Description")->addContent("Logical network used by this appliance.");
2925 }
2926
2927 map<Utf8Str, const VirtualSystemDescriptionEntry*>::const_iterator itS;
2928 uint32_t ulFile = 1;
2929 for (itS = mapDisks.begin();
2930 itS != mapDisks.end();
2931 ++itS)
2932 {
2933 const Utf8Str &strDiskID = itS->first;
2934 const VirtualSystemDescriptionEntry *pDiskEntry = itS->second;
2935
2936 // source path: where the VBox image is
2937 const Utf8Str &strSrcFilePath = pDiskEntry->strVbox;
2938 Bstr bstrSrcFilePath(strSrcFilePath);
2939 if (!RTPathExists(strSrcFilePath.c_str()))
2940 /* This isn't allowed */
2941 throw setError(VBOX_E_FILE_ERROR,
2942 tr("Source virtual disk image file '%s' doesn't exist"),
2943 strSrcFilePath.c_str());
2944
2945 // output filename
2946 const Utf8Str &strTargetFileNameOnly = pDiskEntry->strOvf;
2947 // target path needs to be composed from where the output OVF is
2948 Utf8Str strTargetFilePath = stripFilename(pAppliance->m->strPath);
2949 strTargetFilePath.append("/");
2950 strTargetFilePath.append(strTargetFileNameOnly);
2951
2952 // clone the disk:
2953 ComPtr<IHardDisk> pSourceDisk;
2954 ComPtr<IHardDisk> pTargetDisk;
2955 ComPtr<IProgress> pProgress2;
2956
2957 Log(("Finding source disk \"%ls\"\n", bstrSrcFilePath.raw()));
2958 rc = pVirtualBox->FindHardDisk(bstrSrcFilePath, pSourceDisk.asOutParam());
2959 if (FAILED(rc)) throw rc;
2960
2961 /* We are always exporting to vmdfk stream optimized for now */
2962 Bstr bstrSrcFormat = L"VMDK";
2963
2964 // create a new hard disk interface for the destination disk image
2965 Log(("Creating target disk \"%s\"\n", strTargetFilePath.raw()));
2966 rc = pVirtualBox->CreateHardDisk(bstrSrcFormat, Bstr(strTargetFilePath), pTargetDisk.asOutParam());
2967 if (FAILED(rc)) throw rc;
2968
2969 // the target disk is now registered and needs to be removed again,
2970 // both after successful cloning or if anything goes bad!
2971 try
2972 {
2973 // create a flat copy of the source disk image
2974 rc = pSourceDisk->FlattenTo(pTargetDisk, HardDiskVariant_VmdkStreamOptimized, pProgress2.asOutParam());
2975 if (FAILED(rc)) throw rc;
2976
2977 // advance to the next operation
2978 if (!task->progress.isNull())
2979 task->progress->setNextOperation(BstrFmt(tr("Exporting virtual disk image '%s'"), strSrcFilePath.c_str()),
2980 pDiskEntry->ulSizeMB); // operation's weight, as set up with the IProgress originally);
2981
2982 // now wait for the background disk operation to complete; this throws HRESULTs on error
2983 pAppliance->waitForAsyncProgress(task->progress, pProgress2);
2984 }
2985 catch (HRESULT rc3)
2986 {
2987 // upon error after registering, close the disk or
2988 // it'll stick in the registry forever
2989 pTargetDisk->Close();
2990 throw rc3;
2991 }
2992
2993 // we need the following for the XML
2994 uint64_t cbFile = 0; // actual file size
2995 rc = pTargetDisk->COMGETTER(Size)(&cbFile);
2996 if (FAILED(rc)) throw rc;
2997
2998 ULONG64 cbCapacity = 0; // size reported to guest
2999 rc = pTargetDisk->COMGETTER(LogicalSize)(&cbCapacity);
3000 if (FAILED(rc)) throw rc;
3001 // capacity is reported in megabytes, so...
3002 cbCapacity *= _1M;
3003
3004 // upon success, close the disk as well
3005 rc = pTargetDisk->Close();
3006 if (FAILED(rc)) throw rc;
3007
3008 // now handle the XML for the disk:
3009 Utf8StrFmt strFileRef("file%RI32", ulFile++);
3010 // <File ovf:href="WindowsXpProfessional-disk1.vmdk" ovf:id="file1" ovf:size="1710381056"/>
3011 xml::ElementNode *pelmFile = pelmReferences->createChild("File");
3012 pelmFile->setAttribute("ovf:href", strTargetFileNameOnly);
3013 pelmFile->setAttribute("ovf:id", strFileRef);
3014 pelmFile->setAttribute("ovf:size", Utf8StrFmt("%RI64", cbFile).c_str());
3015
3016 // add disk to XML Disks section
3017 // <Disk ovf:capacity="8589934592" ovf:diskId="vmdisk1" ovf:fileRef="file1" ovf:format="http://www.vmware.com/specifications/vmdk.html#sparse"/>
3018 xml::ElementNode *pelmDisk = pelmDiskSection->createChild("Disk");
3019 pelmDisk->setAttribute("ovf:capacity", Utf8StrFmt("%RI64", cbCapacity).c_str());
3020 pelmDisk->setAttribute("ovf:diskId", strDiskID);
3021 pelmDisk->setAttribute("ovf:fileRef", strFileRef);
3022 pelmDisk->setAttribute("ovf:format", "http://www.vmware.com/specifications/vmdk.html#compressed");
3023 }
3024
3025 // now go write the XML
3026 xml::XmlFileWriter writer(doc);
3027 writer.write(pAppliance->m->strPath.c_str());
3028 }
3029 catch(xml::Error &x)
3030 {
3031 rc = setError(VBOX_E_FILE_ERROR,
3032 x.what());
3033 }
3034 catch(HRESULT aRC)
3035 {
3036 rc = aRC;
3037 }
3038
3039 task->rc = rc;
3040
3041 if (!task->progress.isNull())
3042 task->progress->notifyComplete(rc);
3043
3044 LogFlowFunc(("rc=%Rhrc\n", rc));
3045 LogFlowFuncLeave();
3046
3047 return VINF_SUCCESS;
3048}
3049
3050/**
3051* Public method implementation.
3052 * @return
3053 */
3054STDMETHODIMP Appliance::GetWarnings(ComSafeArrayOut(BSTR, aWarnings))
3055{
3056 if (ComSafeArrayOutIsNull(aWarnings))
3057 return E_POINTER;
3058
3059 AutoCaller autoCaller(this);
3060 CheckComRCReturnRC(autoCaller.rc());
3061
3062 AutoReadLock alock(this);
3063
3064 com::SafeArray<BSTR> sfaWarnings(m->llWarnings.size());
3065
3066 list<Utf8Str>::const_iterator it;
3067 size_t i = 0;
3068 for (it = m->llWarnings.begin();
3069 it != m->llWarnings.end();
3070 ++it, ++i)
3071 {
3072 Bstr bstr = *it;
3073 bstr.cloneTo(&sfaWarnings[i]);
3074 }
3075
3076 sfaWarnings.detachTo(ComSafeArrayOutArg(aWarnings));
3077
3078 return S_OK;
3079}
3080
3081HRESULT Appliance::searchUniqueVMName(Utf8Str& aName) const
3082{
3083 IMachine *machine = NULL;
3084 char *tmpName = RTStrDup(aName.c_str());
3085 int i = 1;
3086 /* @todo: Maybe too cost-intensive; try to find a lighter way */
3087 while (mVirtualBox->FindMachine(Bstr(tmpName), &machine) != VBOX_E_OBJECT_NOT_FOUND)
3088 {
3089 RTStrFree(tmpName);
3090 RTStrAPrintf(&tmpName, "%s_%d", aName.c_str(), i);
3091 ++i;
3092 }
3093 aName = tmpName;
3094 RTStrFree(tmpName);
3095
3096 return S_OK;
3097}
3098
3099HRESULT Appliance::searchUniqueDiskImageFilePath(Utf8Str& aName) const
3100{
3101 IHardDisk *harddisk = NULL;
3102 char *tmpName = RTStrDup(aName.c_str());
3103 int i = 1;
3104 /* Check if the file exists or if a file with this path is registered
3105 * already */
3106 /* @todo: Maybe too cost-intensive; try to find a lighter way */
3107 while (RTPathExists(tmpName) ||
3108 mVirtualBox->FindHardDisk(Bstr(tmpName), &harddisk) != VBOX_E_OBJECT_NOT_FOUND)
3109 {
3110 RTStrFree(tmpName);
3111 char *tmpDir = RTStrDup(aName.c_str());
3112 RTPathStripFilename(tmpDir);;
3113 char *tmpFile = RTStrDup(RTPathFilename(aName.c_str()));
3114 RTPathStripExt(tmpFile);
3115 const char *tmpExt = RTPathExt(aName.c_str());
3116 RTStrAPrintf(&tmpName, "%s%c%s_%d%s", tmpDir, RTPATH_DELIMITER, tmpFile, i, tmpExt);
3117 RTStrFree(tmpFile);
3118 RTStrFree(tmpDir);
3119 ++i;
3120 }
3121 aName = tmpName;
3122 RTStrFree(tmpName);
3123
3124 return S_OK;
3125}
3126
3127/**
3128 * Sets up the given progress object so that it represents disk images accurately
3129 * during importMachines() and write().
3130 * @param pProgress
3131 * @param bstrDescription
3132 * @return
3133 */
3134HRESULT Appliance::setUpProgress(ComObjPtr<Progress> &pProgress, const Bstr &bstrDescription)
3135{
3136 HRESULT rc;
3137
3138 /* Create the progress object */
3139 pProgress.createObject();
3140
3141 // weigh the disk images according to their sizes
3142 uint32_t ulTotalMB = 0;
3143 uint32_t cDisks = 0;
3144 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;
3145 for (it = m->virtualSystemDescriptions.begin();
3146 it != m->virtualSystemDescriptions.end();
3147 ++it)
3148 {
3149 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);
3150 /* One for every hard disk of the Virtual System */
3151 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);
3152 std::list<VirtualSystemDescriptionEntry*>::const_iterator itH;
3153 for (itH = avsdeHDs.begin();
3154 itH != avsdeHDs.end();
3155 ++itH)
3156 {
3157 const VirtualSystemDescriptionEntry *pHD = *itH;
3158 ulTotalMB += pHD->ulSizeMB;
3159 ++cDisks;
3160 }
3161 }
3162
3163 ULONG cOperations = 1 + cDisks; // one op per disk plus 1 for the XML
3164
3165 ULONG ulTotalOperationsWeight;
3166 if (ulTotalMB)
3167 {
3168 ulTotalOperationsWeight = (ULONG)((double)ulTotalMB * 99 / 100); // use 99% of the progress for the disks
3169 m->ulWeightPerOperation = (ULONG)((double)ulTotalMB * 1 / 100); // use 1% of the progress for the XML
3170 }
3171 else
3172 {
3173 // no disks to export:
3174 ulTotalOperationsWeight = 1;
3175 m->ulWeightPerOperation = 1;
3176 }
3177
3178 Log(("Setting up progress object: ulTotalMB = %d, cDisks = %d, => cOperations = %d, ulTotalOperationsWeight = %d, m->ulWeightPerOperation = %d\n",
3179 ulTotalMB, cDisks, cOperations, ulTotalOperationsWeight, m->ulWeightPerOperation));
3180
3181 rc = pProgress->init(mVirtualBox, static_cast<IAppliance*>(this),
3182 bstrDescription,
3183 TRUE /* aCancelable */,
3184 cOperations, // ULONG cOperations,
3185 ulTotalOperationsWeight, // ULONG ulTotalOperationsWeight,
3186 bstrDescription, // CBSTR bstrFirstOperationDescription,
3187 m->ulWeightPerOperation); // ULONG ulFirstOperationWeight,
3188 return rc;
3189}
3190
3191/**
3192 * Called from the import and export background threads to synchronize the second
3193 * background disk thread's progress object with the current progress object so
3194 * that the user interface sees progress correctly and that cancel signals are
3195 * passed on to the second thread.
3196 * @param pProgressThis Progress object of the current thread.
3197 * @param pProgressAsync Progress object of asynchronous task running in background.
3198 */
3199void Appliance::waitForAsyncProgress(ComObjPtr<Progress> &pProgressThis,
3200 ComPtr<IProgress> &pProgressAsync)
3201{
3202 HRESULT rc;
3203
3204 // now loop until the asynchronous operation completes and then report its result
3205 BOOL fCompleted;
3206 BOOL fCanceled;
3207 ULONG currentPercent;
3208 while (SUCCEEDED(pProgressAsync->COMGETTER(Completed(&fCompleted))))
3209 {
3210 rc = pProgressThis->COMGETTER(Canceled)(&fCanceled);
3211 if (FAILED(rc)) throw rc;
3212 if (fCanceled)
3213 {
3214 pProgressAsync->Cancel();
3215 break;
3216 }
3217
3218 rc = pProgressAsync->COMGETTER(Percent(&currentPercent));
3219 if (FAILED(rc)) throw rc;
3220 if (!pProgressThis.isNull())
3221 pProgressThis->setCurrentOperationProgress(currentPercent);
3222 if (fCompleted)
3223 break;
3224
3225 /* Make sure the loop is not too tight */
3226 rc = pProgressAsync->WaitForCompletion(100);
3227 if (FAILED(rc)) throw rc;
3228 }
3229 // report result of asynchronous operation
3230 HRESULT vrc;
3231 rc = pProgressAsync->COMGETTER(ResultCode)(&vrc);
3232 if (FAILED(rc)) throw rc;
3233
3234
3235 // if the thread of the progress object has an error, then
3236 // retrieve the error info from there, or it'll be lost
3237 if (FAILED(vrc))
3238 {
3239 ProgressErrorInfo info(pProgressAsync);
3240 Utf8Str str(info.getText());
3241 const char *pcsz = str.c_str();
3242 HRESULT rc2 = setError(vrc, pcsz);
3243 throw rc2;
3244 }
3245}
3246
3247void Appliance::addWarning(const char* aWarning, ...)
3248{
3249 va_list args;
3250 va_start(args, aWarning);
3251 Utf8StrFmtVA str(aWarning, args);
3252 va_end(args);
3253 m->llWarnings.push_back(str);
3254}
3255
3256////////////////////////////////////////////////////////////////////////////////
3257//
3258// IVirtualSystemDescription constructor / destructor
3259//
3260////////////////////////////////////////////////////////////////////////////////
3261
3262DEFINE_EMPTY_CTOR_DTOR(VirtualSystemDescription)
3263struct shutup3 {};
3264
3265/**
3266 * COM initializer.
3267 * @return
3268 */
3269HRESULT VirtualSystemDescription::init()
3270{
3271 /* Enclose the state transition NotReady->InInit->Ready */
3272 AutoInitSpan autoInitSpan(this);
3273 AssertReturn(autoInitSpan.isOk(), E_FAIL);
3274
3275 /* Initialize data */
3276 m = new Data();
3277
3278 /* Confirm a successful initialization */
3279 autoInitSpan.setSucceeded();
3280 return S_OK;
3281}
3282
3283/**
3284* COM uninitializer.
3285*/
3286
3287void VirtualSystemDescription::uninit()
3288{
3289 delete m;
3290 m = NULL;
3291}
3292
3293////////////////////////////////////////////////////////////////////////////////
3294//
3295// IVirtualSystemDescription public methods
3296//
3297////////////////////////////////////////////////////////////////////////////////
3298
3299/**
3300 * Public method implementation.
3301 * @param
3302 * @return
3303 */
3304STDMETHODIMP VirtualSystemDescription::COMGETTER(Count)(ULONG *aCount)
3305{
3306 if (!aCount)
3307 return E_POINTER;
3308
3309 AutoCaller autoCaller(this);
3310 CheckComRCReturnRC(autoCaller.rc());
3311
3312 AutoReadLock alock(this);
3313
3314 *aCount = (ULONG)m->llDescriptions.size();
3315
3316 return S_OK;
3317}
3318
3319/**
3320 * Public method implementation.
3321 * @return
3322 */
3323STDMETHODIMP VirtualSystemDescription::GetDescription(ComSafeArrayOut(VirtualSystemDescriptionType_T, aTypes),
3324 ComSafeArrayOut(BSTR, aRefs),
3325 ComSafeArrayOut(BSTR, aOrigValues),
3326 ComSafeArrayOut(BSTR, aVboxValues),
3327 ComSafeArrayOut(BSTR, aExtraConfigValues))
3328{
3329 if (ComSafeArrayOutIsNull(aTypes) ||
3330 ComSafeArrayOutIsNull(aRefs) ||
3331 ComSafeArrayOutIsNull(aOrigValues) ||
3332 ComSafeArrayOutIsNull(aVboxValues) ||
3333 ComSafeArrayOutIsNull(aExtraConfigValues))
3334 return E_POINTER;
3335
3336 AutoCaller autoCaller(this);
3337 CheckComRCReturnRC(autoCaller.rc());
3338
3339 AutoReadLock alock(this);
3340
3341 ULONG c = (ULONG)m->llDescriptions.size();
3342 com::SafeArray<VirtualSystemDescriptionType_T> sfaTypes(c);
3343 com::SafeArray<BSTR> sfaRefs(c);
3344 com::SafeArray<BSTR> sfaOrigValues(c);
3345 com::SafeArray<BSTR> sfaVboxValues(c);
3346 com::SafeArray<BSTR> sfaExtraConfigValues(c);
3347
3348 list<VirtualSystemDescriptionEntry>::const_iterator it;
3349 size_t i = 0;
3350 for (it = m->llDescriptions.begin();
3351 it != m->llDescriptions.end();
3352 ++it, ++i)
3353 {
3354 const VirtualSystemDescriptionEntry &vsde = (*it);
3355
3356 sfaTypes[i] = vsde.type;
3357
3358 Bstr bstr = vsde.strRef;
3359 bstr.cloneTo(&sfaRefs[i]);
3360
3361 bstr = vsde.strOvf;
3362 bstr.cloneTo(&sfaOrigValues[i]);
3363
3364 bstr = vsde.strVbox;
3365 bstr.cloneTo(&sfaVboxValues[i]);
3366
3367 bstr = vsde.strExtraConfig;
3368 bstr.cloneTo(&sfaExtraConfigValues[i]);
3369 }
3370
3371 sfaTypes.detachTo(ComSafeArrayOutArg(aTypes));
3372 sfaRefs.detachTo(ComSafeArrayOutArg(aRefs));
3373 sfaOrigValues.detachTo(ComSafeArrayOutArg(aOrigValues));
3374 sfaVboxValues.detachTo(ComSafeArrayOutArg(aVboxValues));
3375 sfaExtraConfigValues.detachTo(ComSafeArrayOutArg(aExtraConfigValues));
3376
3377 return S_OK;
3378}
3379
3380/**
3381 * Public method implementation.
3382 * @return
3383 */
3384STDMETHODIMP VirtualSystemDescription::GetDescriptionByType(VirtualSystemDescriptionType_T aType,
3385 ComSafeArrayOut(VirtualSystemDescriptionType_T, aTypes),
3386 ComSafeArrayOut(BSTR, aRefs),
3387 ComSafeArrayOut(BSTR, aOrigValues),
3388 ComSafeArrayOut(BSTR, aVboxValues),
3389 ComSafeArrayOut(BSTR, aExtraConfigValues))
3390{
3391 if (ComSafeArrayOutIsNull(aTypes) ||
3392 ComSafeArrayOutIsNull(aRefs) ||
3393 ComSafeArrayOutIsNull(aOrigValues) ||
3394 ComSafeArrayOutIsNull(aVboxValues) ||
3395 ComSafeArrayOutIsNull(aExtraConfigValues))
3396 return E_POINTER;
3397
3398 AutoCaller autoCaller(this);
3399 CheckComRCReturnRC(autoCaller.rc());
3400
3401 AutoReadLock alock(this);
3402
3403 std::list<VirtualSystemDescriptionEntry*> vsd = findByType (aType);
3404 ULONG c = (ULONG)vsd.size();
3405 com::SafeArray<VirtualSystemDescriptionType_T> sfaTypes(c);
3406 com::SafeArray<BSTR> sfaRefs(c);
3407 com::SafeArray<BSTR> sfaOrigValues(c);
3408 com::SafeArray<BSTR> sfaVboxValues(c);
3409 com::SafeArray<BSTR> sfaExtraConfigValues(c);
3410
3411 list<VirtualSystemDescriptionEntry*>::const_iterator it;
3412 size_t i = 0;
3413 for (it = vsd.begin();
3414 it != vsd.end();
3415 ++it, ++i)
3416 {
3417 const VirtualSystemDescriptionEntry *vsde = (*it);
3418
3419 sfaTypes[i] = vsde->type;
3420
3421 Bstr bstr = vsde->strRef;
3422 bstr.cloneTo(&sfaRefs[i]);
3423
3424 bstr = vsde->strOvf;
3425 bstr.cloneTo(&sfaOrigValues[i]);
3426
3427 bstr = vsde->strVbox;
3428 bstr.cloneTo(&sfaVboxValues[i]);
3429
3430 bstr = vsde->strExtraConfig;
3431 bstr.cloneTo(&sfaExtraConfigValues[i]);
3432 }
3433
3434 sfaTypes.detachTo(ComSafeArrayOutArg(aTypes));
3435 sfaRefs.detachTo(ComSafeArrayOutArg(aRefs));
3436 sfaOrigValues.detachTo(ComSafeArrayOutArg(aOrigValues));
3437 sfaVboxValues.detachTo(ComSafeArrayOutArg(aVboxValues));
3438 sfaExtraConfigValues.detachTo(ComSafeArrayOutArg(aExtraConfigValues));
3439
3440 return S_OK;
3441}
3442
3443/**
3444 * Public method implementation.
3445 * @return
3446 */
3447STDMETHODIMP VirtualSystemDescription::GetValuesByType(VirtualSystemDescriptionType_T aType,
3448 VirtualSystemDescriptionValueType_T aWhich,
3449 ComSafeArrayOut(BSTR, aValues))
3450{
3451 if (ComSafeArrayOutIsNull(aValues))
3452 return E_POINTER;
3453
3454 AutoCaller autoCaller(this);
3455 CheckComRCReturnRC(autoCaller.rc());
3456
3457 AutoReadLock alock(this);
3458
3459 std::list<VirtualSystemDescriptionEntry*> vsd = findByType (aType);
3460 com::SafeArray<BSTR> sfaValues((ULONG)vsd.size());
3461
3462 list<VirtualSystemDescriptionEntry*>::const_iterator it;
3463 size_t i = 0;
3464 for (it = vsd.begin();
3465 it != vsd.end();
3466 ++it, ++i)
3467 {
3468 const VirtualSystemDescriptionEntry *vsde = (*it);
3469
3470 Bstr bstr;
3471 switch (aWhich)
3472 {
3473 case VirtualSystemDescriptionValueType_Reference: bstr = vsde->strRef; break;
3474 case VirtualSystemDescriptionValueType_Original: bstr = vsde->strOvf; break;
3475 case VirtualSystemDescriptionValueType_Auto: bstr = vsde->strVbox; break;
3476 case VirtualSystemDescriptionValueType_ExtraConfig: bstr = vsde->strExtraConfig; break;
3477 }
3478
3479 bstr.cloneTo(&sfaValues[i]);
3480 }
3481
3482 sfaValues.detachTo(ComSafeArrayOutArg(aValues));
3483
3484 return S_OK;
3485}
3486
3487/**
3488 * Public method implementation.
3489 * @return
3490 */
3491STDMETHODIMP VirtualSystemDescription::SetFinalValues(ComSafeArrayIn(BOOL, aEnabled),
3492 ComSafeArrayIn(IN_BSTR, argVboxValues),
3493 ComSafeArrayIn(IN_BSTR, argExtraConfigValues))
3494{
3495#ifndef RT_OS_WINDOWS
3496 NOREF(aEnabledSize);
3497#endif /* RT_OS_WINDOWS */
3498
3499 CheckComArgSafeArrayNotNull(aEnabled);
3500 CheckComArgSafeArrayNotNull(argVboxValues);
3501 CheckComArgSafeArrayNotNull(argExtraConfigValues);
3502
3503 AutoCaller autoCaller(this);
3504 CheckComRCReturnRC(autoCaller.rc());
3505
3506 AutoWriteLock alock(this);
3507
3508 com::SafeArray<IN_BSTR> aVboxValues(ComSafeArrayInArg(argVboxValues));
3509 com::SafeArray<IN_BSTR> aExtraConfigValues(ComSafeArrayInArg(argExtraConfigValues));
3510
3511 if ( (aVboxValues.size() != m->llDescriptions.size())
3512 || (aExtraConfigValues.size() != m->llDescriptions.size())
3513 )
3514 return E_INVALIDARG;
3515
3516 list<VirtualSystemDescriptionEntry>::iterator it;
3517 size_t i = 0;
3518 for (it = m->llDescriptions.begin();
3519 it != m->llDescriptions.end();
3520 ++it, ++i)
3521 {
3522 VirtualSystemDescriptionEntry& vsde = *it;
3523
3524 if (aEnabled[i])
3525 {
3526 vsde.strVbox = aVboxValues[i];
3527 vsde.strExtraConfig = aExtraConfigValues[i];
3528 }
3529 else
3530 vsde.type = VirtualSystemDescriptionType_Ignore;
3531 }
3532
3533 return S_OK;
3534}
3535
3536/**
3537 * Public method implementation.
3538 * @return
3539 */
3540STDMETHODIMP VirtualSystemDescription::AddDescription(VirtualSystemDescriptionType_T aType,
3541 IN_BSTR aVboxValue,
3542 IN_BSTR aExtraConfigValue)
3543{
3544 CheckComArgNotNull(aVboxValue);
3545 CheckComArgNotNull(aExtraConfigValue);
3546
3547 AutoCaller autoCaller(this);
3548 CheckComRCReturnRC(autoCaller.rc());
3549
3550 AutoWriteLock alock(this);
3551
3552 addEntry(aType, "", aVboxValue, aVboxValue, 0, aExtraConfigValue);
3553
3554 return S_OK;
3555}
3556
3557/**
3558 * Internal method; adds a new description item to the member list.
3559 * @param aType Type of description for the new item.
3560 * @param strRef Reference item; only used with hard disk controllers.
3561 * @param aOrigValue Corresponding original value from OVF.
3562 * @param aAutoValue Initial configuration value (can be overridden by caller with setFinalValues).
3563 * @param strExtraConfig Extra configuration; meaning dependent on type.
3564 */
3565void VirtualSystemDescription::addEntry(VirtualSystemDescriptionType_T aType,
3566 const Utf8Str &strRef,
3567 const Utf8Str &aOrigValue,
3568 const Utf8Str &aAutoValue,
3569 uint32_t ulSizeMB,
3570 const Utf8Str &strExtraConfig /*= ""*/)
3571{
3572 VirtualSystemDescriptionEntry vsde;
3573 vsde.ulIndex = (uint32_t)m->llDescriptions.size(); // each entry gets an index so the client side can reference them
3574 vsde.type = aType;
3575 vsde.strRef = strRef;
3576 vsde.strOvf = aOrigValue;
3577 vsde.strVbox = aAutoValue;
3578 vsde.strExtraConfig = strExtraConfig;
3579 vsde.ulSizeMB = ulSizeMB;
3580
3581 m->llDescriptions.push_back(vsde);
3582}
3583
3584/**
3585 * Private method; returns a list of description items containing all the items from the member
3586 * description items of this virtual system that match the given type.
3587 * @param aType
3588 * @return
3589 */
3590std::list<VirtualSystemDescriptionEntry*> VirtualSystemDescription::findByType(VirtualSystemDescriptionType_T aType)
3591{
3592 std::list<VirtualSystemDescriptionEntry*> vsd;
3593
3594 list<VirtualSystemDescriptionEntry>::iterator it;
3595 for (it = m->llDescriptions.begin();
3596 it != m->llDescriptions.end();
3597 ++it)
3598 {
3599 if (it->type == aType)
3600 vsd.push_back(&(*it));
3601 }
3602
3603 return vsd;
3604}
3605
3606/**
3607 * Private method; looks thru the member hardware items for the IDE, SATA, or SCSI controller with
3608 * the given reference ID. Useful when needing the controller for a particular
3609 * virtual disk.
3610 * @param id
3611 * @return
3612 */
3613const VirtualSystemDescriptionEntry* VirtualSystemDescription::findControllerFromID(uint32_t id)
3614{
3615 Utf8Str strRef = Utf8StrFmt("%RI32", id);
3616 list<VirtualSystemDescriptionEntry>::const_iterator it;
3617 for (it = m->llDescriptions.begin();
3618 it != m->llDescriptions.end();
3619 ++it)
3620 {
3621 const VirtualSystemDescriptionEntry &d = *it;
3622 switch (d.type)
3623 {
3624 case VirtualSystemDescriptionType_HardDiskControllerIDE:
3625 case VirtualSystemDescriptionType_HardDiskControllerSATA:
3626 case VirtualSystemDescriptionType_HardDiskControllerSCSI:
3627 if (d.strRef == strRef)
3628 return &d;
3629 break;
3630 }
3631 }
3632
3633 return NULL;
3634}
3635
3636////////////////////////////////////////////////////////////////////////////////
3637//
3638// IMachine public methods
3639//
3640////////////////////////////////////////////////////////////////////////////////
3641
3642// This code is here so we won't have to include the appliance headers in the
3643// IMachine implementation, and we also need to access private appliance data.
3644
3645/**
3646* Public method implementation.
3647* @param appliance
3648* @return
3649*/
3650
3651STDMETHODIMP Machine::Export(IAppliance *aAppliance, IVirtualSystemDescription **aDescription)
3652{
3653 HRESULT rc = S_OK;
3654
3655 if (!aAppliance)
3656 return E_POINTER;
3657
3658 AutoCaller autoCaller(this);
3659 CheckComRCReturnRC(autoCaller.rc());
3660
3661 AutoReadLock alock(this);
3662
3663 ComObjPtr<VirtualSystemDescription> pNewDesc;
3664
3665 try
3666 {
3667 Bstr bstrName;
3668 Bstr bstrDescription;
3669 Bstr bstrGuestOSType;
3670 uint32_t cCPUs;
3671 uint32_t ulMemSizeMB;
3672 BOOL fDVDEnabled;
3673 BOOL fFloppyEnabled;
3674 BOOL fUSBEnabled;
3675 BOOL fAudioEnabled;
3676 AudioControllerType_T audioController;
3677
3678 ComPtr<IUSBController> pUsbController;
3679 ComPtr<IAudioAdapter> pAudioAdapter;
3680
3681 // get name
3682 bstrName = mUserData->mName;
3683 // get description
3684 bstrDescription = mUserData->mDescription;
3685 // get guest OS
3686 bstrGuestOSType = mUserData->mOSTypeId;
3687 // CPU count
3688 cCPUs = mHWData->mCPUCount;
3689 // memory size in MB
3690 ulMemSizeMB = mHWData->mMemorySize;
3691 // VRAM size?
3692 // BIOS settings?
3693 // 3D acceleration enabled?
3694 // hardware virtualization enabled?
3695 // nested paging enabled?
3696 // HWVirtExVPIDEnabled?
3697 // PAEEnabled?
3698 // snapshotFolder?
3699 // VRDPServer?
3700
3701 // floppy
3702 rc = mFloppyDrive->COMGETTER(Enabled)(&fFloppyEnabled);
3703 if (FAILED(rc)) throw rc;
3704
3705 // CD-ROM ?!?
3706 // ComPtr<IDVDDrive> pDVDDrive;
3707 fDVDEnabled = 1;
3708
3709 // this is more tricky so use the COM method
3710 rc = COMGETTER(USBController)(pUsbController.asOutParam());
3711 if (FAILED(rc)) throw rc;
3712 rc = pUsbController->COMGETTER(Enabled)(&fUSBEnabled);
3713
3714 pAudioAdapter = mAudioAdapter;
3715 rc = pAudioAdapter->COMGETTER(Enabled)(&fAudioEnabled);
3716 if (FAILED(rc)) throw rc;
3717 rc = pAudioAdapter->COMGETTER(AudioController)(&audioController);
3718 if (FAILED(rc)) throw rc;
3719
3720 // create a new virtual system
3721 rc = pNewDesc.createObject();
3722 CheckComRCThrowRC(rc);
3723 rc = pNewDesc->init();
3724 CheckComRCThrowRC(rc);
3725
3726 /* Guest OS type */
3727 Utf8Str strOsTypeVBox(bstrGuestOSType);
3728 CIMOSType_T cim = convertVBoxOSType2CIMOSType(strOsTypeVBox.c_str());
3729 pNewDesc->addEntry(VirtualSystemDescriptionType_OS,
3730 "",
3731 Utf8StrFmt("%RI32", cim),
3732 strOsTypeVBox);
3733
3734 /* VM name */
3735 Utf8Str strVMName(bstrName);
3736 pNewDesc->addEntry(VirtualSystemDescriptionType_Name,
3737 "",
3738 strVMName,
3739 strVMName);
3740
3741 // description
3742 Utf8Str strDescription(bstrDescription);
3743 pNewDesc->addEntry(VirtualSystemDescriptionType_Description,
3744 "",
3745 strDescription,
3746 strDescription);
3747
3748 /* CPU count*/
3749 Utf8Str strCpuCount = Utf8StrFmt("%RI32", cCPUs);
3750 pNewDesc->addEntry(VirtualSystemDescriptionType_CPU,
3751 "",
3752 strCpuCount,
3753 strCpuCount);
3754
3755 /* Memory */
3756 Utf8Str strMemory = Utf8StrFmt("%RI32", (uint64_t)ulMemSizeMB * _1M);
3757 pNewDesc->addEntry(VirtualSystemDescriptionType_Memory,
3758 "",
3759 strMemory,
3760 strMemory);
3761
3762 int32_t lIDEControllerIndex = 0;
3763 int32_t lSATAControllerIndex = 0;
3764 int32_t lSCSIControllerIndex = 0;
3765
3766// <const name="HardDiskControllerIDE" value="6" />
3767 ComPtr<IStorageController> pController;
3768 rc = GetStorageControllerByName(Bstr("IDE"), pController.asOutParam());
3769 if (FAILED(rc)) throw rc;
3770 Utf8Str strVbox;
3771 StorageControllerType_T ctlr;
3772 rc = pController->COMGETTER(ControllerType)(&ctlr);
3773 if (FAILED(rc)) throw rc;
3774 switch(ctlr)
3775 {
3776 case StorageControllerType_PIIX3: strVbox = "PIIX3"; break;
3777 case StorageControllerType_PIIX4: strVbox = "PIIX4"; break;
3778 case StorageControllerType_ICH6: strVbox = "ICH6"; break;
3779 }
3780
3781 if (strVbox.length())
3782 {
3783 lIDEControllerIndex = (int32_t)pNewDesc->m->llDescriptions.size();
3784 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerIDE,
3785 Utf8StrFmt("%d", lIDEControllerIndex),
3786 strVbox,
3787 strVbox);
3788 }
3789
3790#ifdef VBOX_WITH_AHCI
3791// <const name="HardDiskControllerSATA" value="7" />
3792 rc = GetStorageControllerByName(Bstr("SATA"), pController.asOutParam());
3793 if (SUCCEEDED(rc))
3794 {
3795 strVbox = "AHCI";
3796 lSATAControllerIndex = (int32_t)pNewDesc->m->llDescriptions.size();
3797 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSATA,
3798 Utf8StrFmt("%d", lSATAControllerIndex),
3799 strVbox,
3800 strVbox);
3801 }
3802#endif // VBOX_WITH_AHCI
3803
3804// <const name="HardDiskControllerSCSI" value="8" />
3805 rc = GetStorageControllerByName(Bstr("SCSI"), pController.asOutParam());
3806 if (SUCCEEDED(rc))
3807 {
3808 rc = pController->COMGETTER(ControllerType)(&ctlr);
3809 if (SUCCEEDED(rc))
3810 {
3811 strVbox = "LsiLogic"; // the default in VBox
3812 switch(ctlr)
3813 {
3814 case StorageControllerType_LsiLogic: strVbox = "LsiLogic"; break;
3815 case StorageControllerType_BusLogic: strVbox = "BusLogic"; break;
3816 }
3817 lSCSIControllerIndex = (int32_t)pNewDesc->m->llDescriptions.size();
3818 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSCSI,
3819 Utf8StrFmt("%d", lSCSIControllerIndex),
3820 strVbox,
3821 strVbox);
3822 }
3823 else
3824 throw rc;
3825 }
3826
3827// <const name="HardDiskImage" value="9" />
3828 HDData::AttachmentList::iterator itA;
3829 for (itA = mHDData->mAttachments.begin();
3830 itA != mHDData->mAttachments.end();
3831 ++itA)
3832 {
3833 ComObjPtr<HardDiskAttachment> pHDA = *itA;
3834
3835 // the attachment's data
3836 ComPtr<IHardDisk> pHardDisk;
3837 ComPtr<IStorageController> ctl;
3838 Bstr controllerName;
3839
3840 rc = pHDA->COMGETTER(Controller)(controllerName.asOutParam());
3841 if (FAILED(rc)) throw rc;
3842
3843 rc = GetStorageControllerByName(controllerName, ctl.asOutParam());
3844 if (FAILED(rc)) throw rc;
3845
3846 StorageBus_T storageBus;
3847 LONG lChannel;
3848 LONG lDevice;
3849
3850 rc = ctl->COMGETTER(Bus)(&storageBus);
3851 if (FAILED(rc)) throw rc;
3852
3853 rc = pHDA->COMGETTER(HardDisk)(pHardDisk.asOutParam());
3854 if (FAILED(rc)) throw rc;
3855
3856 rc = pHDA->COMGETTER(Port)(&lChannel);
3857 if (FAILED(rc)) throw rc;
3858
3859 rc = pHDA->COMGETTER(Device)(&lDevice);
3860 if (FAILED(rc)) throw rc;
3861
3862 Bstr bstrLocation;
3863 rc = pHardDisk->COMGETTER(Location)(bstrLocation.asOutParam());
3864 if (FAILED(rc)) throw rc;
3865 Bstr bstrName;
3866 rc = pHardDisk->COMGETTER(Name)(bstrName.asOutParam());
3867 if (FAILED(rc)) throw rc;
3868
3869 // force reading state, or else size will be returned as 0
3870 MediaState_T ms;
3871 rc = pHardDisk->COMGETTER(State)(&ms);
3872 if (FAILED(rc)) throw rc;
3873
3874 ULONG64 ullSize;
3875 rc = pHardDisk->COMGETTER(Size)(&ullSize);
3876 if (FAILED(rc)) throw rc;
3877
3878 // and how this translates to the virtual system
3879 int32_t lControllerVsys = 0;
3880 LONG lChannelVsys;
3881
3882 switch (storageBus)
3883 {
3884 case StorageBus_IDE:
3885 // this is the exact reverse to what we're doing in Appliance::taskThreadImportMachines,
3886 // and it must be updated when that is changed!
3887
3888 if (lChannel == 0 && lDevice == 0) // primary master
3889 lChannelVsys = 0;
3890 else if (lChannel == 0 && lDevice == 1) // primary slave
3891 lChannelVsys = 1;
3892 else if (lChannel == 1 && lDevice == 1) // secondary slave; secondary master is always CDROM
3893 lChannelVsys = 2;
3894 else
3895 throw setError(VBOX_E_NOT_SUPPORTED,
3896 tr("Cannot handle hard disk attachment: channel is %d, device is %d"), lChannel, lDevice);
3897
3898 lControllerVsys = lIDEControllerIndex;
3899 break;
3900
3901 case StorageBus_SATA:
3902 lChannelVsys = lChannel; // should be between 0 and 29
3903 lControllerVsys = lSATAControllerIndex;
3904 break;
3905
3906 case StorageBus_SCSI:
3907 lChannelVsys = lChannel; // should be between 0 and 15
3908 lControllerVsys = lSCSIControllerIndex;
3909 break;
3910
3911 default:
3912 throw setError(VBOX_E_NOT_SUPPORTED,
3913 tr("Cannot handle hard disk attachment: storageBus is %d, channel is %d, device is %d"), storageBus, lChannel, lDevice);
3914 break;
3915 }
3916
3917 Utf8Str strTargetVmdkName(bstrName);
3918 RTPathStripExt(strTargetVmdkName.mutableRaw());
3919 strTargetVmdkName.append(".vmdk");
3920
3921 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskImage,
3922 strTargetVmdkName, // disk ID: let's use the name
3923 strTargetVmdkName, // OVF value:
3924 Utf8Str(bstrLocation), // vbox value: media path
3925 (uint32_t)(ullSize / _1M),
3926 Utf8StrFmt("controller=%RI32;channel=%RI32", lControllerVsys, lChannelVsys));
3927 }
3928
3929 /* Floppy Drive */
3930 if (fFloppyEnabled)
3931 pNewDesc->addEntry(VirtualSystemDescriptionType_Floppy, "", "", "");
3932
3933 /* CD Drive */
3934 if (fDVDEnabled)
3935 pNewDesc->addEntry(VirtualSystemDescriptionType_CDROM, "", "", "");
3936
3937// <const name="NetworkAdapter" />
3938 size_t a;
3939 for (a = 0;
3940 a < SchemaDefs::NetworkAdapterCount;
3941 ++a)
3942 {
3943 ComPtr<INetworkAdapter> pNetworkAdapter;
3944 BOOL fEnabled;
3945 NetworkAdapterType_T adapterType;
3946 NetworkAttachmentType_T attachmentType;
3947
3948 rc = GetNetworkAdapter((ULONG)a, pNetworkAdapter.asOutParam());
3949 if (FAILED(rc)) throw rc;
3950 /* Enable the network card & set the adapter type */
3951 rc = pNetworkAdapter->COMGETTER(Enabled)(&fEnabled);
3952 if (FAILED(rc)) throw rc;
3953
3954 if (fEnabled)
3955 {
3956 Utf8Str strAttachmentType;
3957
3958 rc = pNetworkAdapter->COMGETTER(AdapterType)(&adapterType);
3959 if (FAILED(rc)) throw rc;
3960
3961 rc = pNetworkAdapter->COMGETTER(AttachmentType)(&attachmentType);
3962 if (FAILED(rc)) throw rc;
3963
3964 switch (attachmentType)
3965 {
3966 case NetworkAttachmentType_Null:
3967 strAttachmentType = "Null";
3968 break;
3969
3970 case NetworkAttachmentType_NAT:
3971 strAttachmentType = "NAT";
3972 break;
3973
3974 case NetworkAttachmentType_Bridged:
3975 strAttachmentType = "Bridged";
3976 break;
3977
3978 case NetworkAttachmentType_Internal:
3979 strAttachmentType = "Internal";
3980 break;
3981
3982 case NetworkAttachmentType_HostOnly:
3983 strAttachmentType = "HostOnly";
3984 break;
3985 }
3986
3987 pNewDesc->addEntry(VirtualSystemDescriptionType_NetworkAdapter,
3988 "", // ref
3989 strAttachmentType, // orig
3990 Utf8StrFmt("%RI32", (uint32_t)adapterType), // conf
3991 Utf8StrFmt("type=%s", strAttachmentType.c_str())); // extra conf
3992 }
3993 }
3994
3995// <const name="USBController" />
3996#ifdef VBOX_WITH_USB
3997 if (fUSBEnabled)
3998 pNewDesc->addEntry(VirtualSystemDescriptionType_USBController, "", "", "");
3999#endif /* VBOX_WITH_USB */
4000
4001// <const name="SoundCard" />
4002 if (fAudioEnabled)
4003 {
4004 pNewDesc->addEntry(VirtualSystemDescriptionType_SoundCard,
4005 "",
4006 "ensoniq1371", // this is what OVFTool writes and VMware supports
4007 Utf8StrFmt("%RI32", audioController));
4008 }
4009
4010 // finally, add the virtual system to the appliance
4011 Appliance *pAppliance = static_cast<Appliance*>(aAppliance);
4012 AutoCaller autoCaller(pAppliance);
4013 if (FAILED(rc)) throw rc;
4014
4015 /* We return the new description to the caller */
4016 ComPtr<IVirtualSystemDescription> copy(pNewDesc);
4017 copy.queryInterfaceTo(aDescription);
4018
4019 AutoWriteLock alock(pAppliance);
4020
4021 pAppliance->m->virtualSystemDescriptions.push_back(pNewDesc);
4022 }
4023 catch(HRESULT arc)
4024 {
4025 rc = arc;
4026 }
4027
4028 return rc;
4029}
4030
4031/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

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