VirtualBox

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

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

OVF: fix progress values for disks on import

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