VirtualBox

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

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

OVF: import/export IDE controller subtype (PIIX3/4) in XML to prevent windows guest bluescreens

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 174.3 KB
Line 
1/* $Id: ApplianceImpl.cpp 18350 2009-03-26 20:11:02Z vboxsync $ */
2/** @file
3 *
4 * IAppliance and IVirtualSystem COM class implementations.
5 */
6
7/*
8 * Copyright (C) 2008-2009 Sun Microsystems, Inc.
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
19 * Clara, CA 95054 USA or visit http://www.sun.com if you need
20 * additional information or have any questions.
21 */
22
23#include <VBox/param.h>
24#include <iprt/stream.h>
25#include <iprt/path.h>
26#include <iprt/dir.h>
27#include <iprt/file.h>
28
29#include "ApplianceImpl.h"
30#include "VirtualBoxImpl.h"
31#include "GuestOSTypeImpl.h"
32#include "ProgressImpl.h"
33#include "MachineImpl.h"
34#include "HostNetworkInterfaceImpl.h"
35
36#include "Logging.h"
37
38#include "VBox/xml.h"
39
40using namespace std;
41
42////////////////////////////////////////////////////////////////////////////////
43//
44// hardware definitions
45//
46////////////////////////////////////////////////////////////////////////////////
47
48struct DiskImage
49{
50 Utf8Str strDiskId; // value from DiskSection/Disk/@diskId
51 int64_t iCapacity; // value from DiskSection/Disk/@capacity;
52 // (maximum size for dynamic images, I guess; we always translate this to bytes)
53 int64_t iPopulatedSize; // optional value from DiskSection/Disk/@populatedSize
54 // (actual used size of disk, always in bytes; can be an estimate of used disk
55 // space, but cannot be larger than iCapacity; -1 if not set)
56 Utf8Str strFormat; // value from DiskSection/Disk/@format
57 // typically http://www.vmware.com/specifications/vmdk.html#sparse
58
59 // fields from /References/File; the spec says the file reference from disk can be empty,
60 // so in that case, strFilename will be empty, then a new disk should be created
61 Utf8Str strHref; // value from /References/File/@href (filename); if empty, then the remaining fields are ignored
62 int64_t iSize; // value from /References/File/@size (optional according to spec; then we set -1 here)
63 int64_t iChunkSize; // value from /References/File/@chunkSize (optional, unsupported)
64 Utf8Str strCompression; // value from /References/File/@compression (optional, can be "gzip" according to spec)
65};
66
67struct VirtualHardwareItem
68{
69 Utf8Str strDescription;
70 Utf8Str strCaption;
71 Utf8Str strElementName;
72
73 uint32_t ulInstanceID;
74 uint32_t ulParent;
75
76 OVFResourceType_T resourceType;
77 Utf8Str strOtherResourceType;
78 Utf8Str strResourceSubType;
79
80 Utf8Str strHostResource; // "Abstractly specifies how a device shall connect to a resource on the deployment platform.
81 // Not all devices need a backing." Used with disk items, for which this references a virtual
82 // disk from the Disks section.
83 bool fAutomaticAllocation;
84 bool fAutomaticDeallocation;
85 Utf8Str strConnection; // "All Ethernet adapters that specify the same abstract network connection name within an OVF
86 // package shall be deployed on the same network. The abstract network connection name shall be
87 // listed in the NetworkSection at the outermost envelope level." We ignore this and only set up
88 // a network adapter depending on the network name.
89 Utf8Str strAddress; // "Device-specific. For an Ethernet adapter, this specifies the MAC address."
90 Utf8Str strAddressOnParent; // "For a device, this specifies its location on the controller."
91 Utf8Str strAllocationUnits; // "Specifies the units of allocation used. For example, “byte * 2^20”."
92 uint64_t ullVirtualQuantity; // "Specifies the quantity of resources presented. For example, “256”."
93 uint64_t ullReservation; // "Specifies the minimum quantity of resources guaranteed to be available."
94 uint64_t ullLimit; // "Specifies the maximum quantity of resources that will be granted."
95 uint64_t ullWeight; // "Specifies a relative priority for this allocation in relation to other allocations."
96
97 Utf8Str strConsumerVisibility;
98 Utf8Str strMappingBehavior;
99 Utf8Str strPoolID;
100 uint32_t ulBusNumber; // seen with IDE controllers, but not listed in OVF spec
101
102 uint32_t ulLineNumber; // line number of <Item> element in XML source; cached for error messages
103
104 VirtualHardwareItem()
105 : ulInstanceID(0), fAutomaticAllocation(false), fAutomaticDeallocation(false), ullVirtualQuantity(0), ullReservation(0), ullLimit(0), ullWeight(0), ulBusNumber(0), ulLineNumber(0)
106 {};
107};
108
109typedef map<Utf8Str, DiskImage> DiskImagesMap;
110
111struct VirtualSystem;
112
113typedef map<uint32_t, VirtualHardwareItem> HardwareItemsMap;
114
115struct HardDiskController
116{
117 uint32_t idController; // instance ID (Item/InstanceId); this gets referenced from HardDisk
118 enum ControllerSystemType { IDE, SATA, SCSI };
119 ControllerSystemType system; // one of IDE, SATA, SCSI
120 Utf8Str strControllerType; // controller subtype (Item/ResourceSubType); e.g. "LsiLogic"; can be empty (esp. for IDE)
121 Utf8Str strAddress; // for IDE
122 uint32_t ulBusNumber; // for IDE
123
124 HardDiskController()
125 : idController(0),
126 ulBusNumber(0)
127 {
128 }
129};
130
131typedef map<uint32_t, HardDiskController> ControllersMap;
132
133struct VirtualDisk
134{
135 uint32_t idController; // SCSI (or IDE) controller this disk is connected to;
136 // points into VirtualSystem.mapControllers
137 uint32_t ulAddressOnParent; // parsed strAddressOnParent of hardware item; will be 0 or 1 for IDE
138 // and possibly higher for disks attached to SCSI controllers (untested)
139 Utf8Str strDiskId; // if the hard disk has an ovf:/disk/<id> reference,
140 // this receives the <id> component; points to one of the
141 // references in Appliance::Data.mapDisks
142};
143
144typedef map<Utf8Str, VirtualDisk> VirtualDisksMap;
145
146struct EthernetAdapter
147{
148 Utf8Str strAdapterType; // "PCNet32" or "E1000" or whatever; from <rasd:ResourceSubType>
149 Utf8Str strNetworkName; // from <rasd:Connection>
150};
151
152typedef list<EthernetAdapter> EthernetAdaptersList;
153
154struct VirtualSystem
155{
156 Utf8Str strName; // copy of VirtualSystem/@id
157
158 Utf8Str strDescription; // copy of VirtualSystem/Info content
159
160 CIMOSType_T cimos;
161 Utf8Str strVirtualSystemType; // generic hardware description; OVF says this can be something like "vmx-4" or "xen";
162 // VMware Workstation 6.5 is "vmx-07"
163
164 HardwareItemsMap mapHardwareItems; // map of virtual hardware items, sorted by unique instance ID
165
166 uint64_t ullMemorySize; // always in bytes, copied from llHardwareItems; default = 0 (unspecified)
167 uint16_t cCPUs; // no. of CPUs, copied from llHardwareItems; default = 1
168
169 EthernetAdaptersList llEthernetAdapters; // (one for each VirtualSystem/Item[@ResourceType=10]element)
170
171 ControllersMap mapControllers;
172 // list of hard disk controllers
173 // (one for each VirtualSystem/Item[@ResourceType=6] element with accumulated data from children)
174
175 VirtualDisksMap mapVirtualDisks;
176 // (one for each VirtualSystem/Item[@ResourceType=17] element with accumulated data from children)
177
178 bool fHasFloppyDrive; // true if there's a floppy item in mapHardwareItems
179 bool fHasCdromDrive; // true if there's a CD-ROM item in mapHardwareItems; ISO images are not yet supported by OVFtool
180 bool fHasUsbController; // true if there's a USB controller item in mapHardwareItems
181
182 Utf8Str strSoundCardType; // if not empty, then the system wants a soundcard; this then specifies the hardware;
183 // VMware Workstation 6.5 uses "ensoniq1371" for example
184
185 Utf8Str strLicenseInfo; // license info if any; receives contents of VirtualSystem/EulaSection/Info
186 Utf8Str strLicenseText; // license info if any; receives contents of VirtualSystem/EulaSection/License
187
188 VirtualSystem()
189 : ullMemorySize(0), cCPUs(1), fHasFloppyDrive(false), fHasCdromDrive(false), fHasUsbController(false)
190 {
191 }
192};
193
194////////////////////////////////////////////////////////////////////////////////
195//
196// Appliance data definition
197//
198////////////////////////////////////////////////////////////////////////////////
199
200// opaque private instance data of Appliance class
201struct Appliance::Data
202{
203 Utf8Str strPath; // file name last given to either read() or write()
204
205 DiskImagesMap mapDisks; // map of DiskImage structs, sorted by DiskImage.strDiskId
206
207 list<VirtualSystem> llVirtualSystems; // list of virtual systems, created by and valid after read()
208
209 list< ComObjPtr<VirtualSystemDescription> > virtualSystemDescriptions; //
210
211 list<Utf8Str> llWarnings;
212
213 ULONG ulWeightPerOperation; // for progress calculations
214};
215
216struct VirtualSystemDescription::Data
217{
218 list<VirtualSystemDescriptionEntry> llDescriptions;
219};
220
221////////////////////////////////////////////////////////////////////////////////
222//
223// internal helpers
224//
225////////////////////////////////////////////////////////////////////////////////
226
227static Utf8Str stripFilename(const Utf8Str &strFile)
228{
229 Utf8Str str2(strFile);
230 RTPathStripFilename(str2.mutableRaw());
231 return str2;
232}
233
234static const struct
235{
236 CIMOSType_T cim;
237 const char *pcszVbox;
238}
239 g_osTypes[] =
240 {
241 { CIMOSType_CIMOS_Unknown, SchemaDefs_OSTypeId_Other },
242 { CIMOSType_CIMOS_OS2, SchemaDefs_OSTypeId_OS2 },
243 { CIMOSType_CIMOS_MSDOS, SchemaDefs_OSTypeId_DOS },
244 { CIMOSType_CIMOS_WIN3x, SchemaDefs_OSTypeId_Windows31 },
245 { CIMOSType_CIMOS_WIN95, SchemaDefs_OSTypeId_Windows95 },
246 { CIMOSType_CIMOS_WIN98, SchemaDefs_OSTypeId_Windows98 },
247 { CIMOSType_CIMOS_WINNT, SchemaDefs_OSTypeId_WindowsNT4 },
248 { CIMOSType_CIMOS_NetWare, SchemaDefs_OSTypeId_Netware },
249 { CIMOSType_CIMOS_NovellOES, SchemaDefs_OSTypeId_Netware },
250 { CIMOSType_CIMOS_Solaris, SchemaDefs_OSTypeId_OpenSolaris },
251 { CIMOSType_CIMOS_SunOS, SchemaDefs_OSTypeId_OpenSolaris },
252 { CIMOSType_CIMOS_FreeBSD, SchemaDefs_OSTypeId_FreeBSD },
253 { CIMOSType_CIMOS_NetBSD, SchemaDefs_OSTypeId_NetBSD },
254 { CIMOSType_CIMOS_QNX, SchemaDefs_OSTypeId_QNX },
255 { CIMOSType_CIMOS_Windows2000, SchemaDefs_OSTypeId_Windows2000 },
256 { CIMOSType_CIMOS_WindowsMe, SchemaDefs_OSTypeId_WindowsMe },
257 { CIMOSType_CIMOS_OpenBSD, SchemaDefs_OSTypeId_OpenBSD },
258 { CIMOSType_CIMOS_WindowsXP, SchemaDefs_OSTypeId_WindowsXP },
259 { CIMOSType_CIMOS_WindowsXPEmbedded, SchemaDefs_OSTypeId_WindowsXP },
260 { CIMOSType_CIMOS_WindowsEmbeddedforPointofService, SchemaDefs_OSTypeId_WindowsXP },
261 { CIMOSType_CIMOS_MicrosoftWindowsServer2003, SchemaDefs_OSTypeId_Windows2003 },
262 { CIMOSType_CIMOS_MicrosoftWindowsServer2003_64, SchemaDefs_OSTypeId_Windows2003_64 },
263 { CIMOSType_CIMOS_WindowsXP_64, SchemaDefs_OSTypeId_WindowsXP_64 },
264 { CIMOSType_CIMOS_WindowsVista, SchemaDefs_OSTypeId_WindowsVista },
265 { CIMOSType_CIMOS_WindowsVista_64, SchemaDefs_OSTypeId_WindowsVista_64 },
266 { CIMOSType_CIMOS_MicrosoftWindowsServer2008, SchemaDefs_OSTypeId_Windows2008 },
267 { CIMOSType_CIMOS_MicrosoftWindowsServer2008_64, SchemaDefs_OSTypeId_Windows2008_64 },
268 { CIMOSType_CIMOS_FreeBSD_64, SchemaDefs_OSTypeId_FreeBSD_64 },
269 { CIMOSType_CIMOS_RedHatEnterpriseLinux, SchemaDefs_OSTypeId_RedHat },
270 { CIMOSType_CIMOS_RedHatEnterpriseLinux_64, SchemaDefs_OSTypeId_RedHat_64 },
271 { CIMOSType_CIMOS_Solaris_64, SchemaDefs_OSTypeId_OpenSolaris_64 },
272 { CIMOSType_CIMOS_SUSE, SchemaDefs_OSTypeId_OpenSUSE },
273 { CIMOSType_CIMOS_SLES, SchemaDefs_OSTypeId_OpenSUSE },
274 { CIMOSType_CIMOS_NovellLinuxDesktop, SchemaDefs_OSTypeId_OpenSUSE },
275 { CIMOSType_CIMOS_SUSE_64, SchemaDefs_OSTypeId_OpenSUSE_64 },
276 { CIMOSType_CIMOS_SLES_64, SchemaDefs_OSTypeId_OpenSUSE_64 },
277 { CIMOSType_CIMOS_LINUX, SchemaDefs_OSTypeId_Linux },
278 { CIMOSType_CIMOS_SunJavaDesktopSystem, SchemaDefs_OSTypeId_Linux },
279 { CIMOSType_CIMOS_TurboLinux, SchemaDefs_OSTypeId_Linux},
280
281 // { CIMOSType_CIMOS_TurboLinux_64, },
282
283 { CIMOSType_CIMOS_Mandriva, SchemaDefs_OSTypeId_Mandriva },
284 { CIMOSType_CIMOS_Mandriva_64, SchemaDefs_OSTypeId_Mandriva_64 },
285 { CIMOSType_CIMOS_Ubuntu, SchemaDefs_OSTypeId_Ubuntu },
286 { CIMOSType_CIMOS_Ubuntu_64, SchemaDefs_OSTypeId_Ubuntu_64 },
287 { CIMOSType_CIMOS_Debian, SchemaDefs_OSTypeId_Debian },
288 { CIMOSType_CIMOS_Debian_64, SchemaDefs_OSTypeId_Debian_64 },
289 { CIMOSType_CIMOS_Linux_2_4_x, SchemaDefs_OSTypeId_Linux24 },
290 { CIMOSType_CIMOS_Linux_2_4_x_64, SchemaDefs_OSTypeId_Linux24_64 },
291 { CIMOSType_CIMOS_Linux_2_6_x, SchemaDefs_OSTypeId_Linux26 },
292 { CIMOSType_CIMOS_Linux_2_6_x_64, SchemaDefs_OSTypeId_Linux26_64 },
293 { CIMOSType_CIMOS_Linux_64, SchemaDefs_OSTypeId_Linux26_64 }
294};
295
296/**
297 * Private helper func that suggests a VirtualBox guest OS type
298 * for the given OVF operating system type.
299 * @param osTypeVBox
300 * @param c
301 */
302static void convertCIMOSType2VBoxOSType(Utf8Str &strType, CIMOSType_T c)
303{
304 for (size_t i = 0; i < RT_ELEMENTS(g_osTypes); ++i)
305 {
306 if (c == g_osTypes[i].cim)
307 {
308 strType = g_osTypes[i].pcszVbox;
309 return;
310 }
311 }
312
313 strType = SchemaDefs_OSTypeId_Other;
314}
315
316/**
317 * Private helper func that suggests a VirtualBox guest OS type
318 * for the given OVF operating system type.
319 * @param osTypeVBox
320 * @param c
321 */
322static CIMOSType_T convertVBoxOSType2CIMOSType(const char *pcszVbox)
323{
324 for (size_t i = 0; i < RT_ELEMENTS(g_osTypes); ++i)
325 {
326 if (!RTStrICmp(pcszVbox, g_osTypes[i].pcszVbox))
327 return g_osTypes[i].cim;
328 }
329
330 return CIMOSType_CIMOS_Other;
331}
332
333////////////////////////////////////////////////////////////////////////////////
334//
335// IVirtualBox public methods
336//
337////////////////////////////////////////////////////////////////////////////////
338
339// This code is here so we won't have to include the appliance headers in the
340// IVirtualBox implementation.
341
342/**
343 * Implementation for IVirtualBox::createAppliance.
344 *
345 * @param anAppliance IAppliance object created if S_OK is returned.
346 * @return S_OK or error.
347 */
348STDMETHODIMP VirtualBox::CreateAppliance(IAppliance** anAppliance)
349{
350 HRESULT rc;
351
352 ComObjPtr<Appliance> appliance;
353 appliance.createObject();
354 rc = appliance->init(this);
355
356 if (SUCCEEDED(rc))
357 appliance.queryInterfaceTo(anAppliance);
358
359 return rc;
360}
361
362////////////////////////////////////////////////////////////////////////////////
363//
364// Appliance constructor / destructor
365//
366////////////////////////////////////////////////////////////////////////////////
367
368DEFINE_EMPTY_CTOR_DTOR(Appliance)
369struct shutup {};
370
371/**
372 * Appliance COM initializer.
373 * @param
374 * @return
375 */
376
377HRESULT Appliance::init(VirtualBox *aVirtualBox)
378{
379 /* Enclose the state transition NotReady->InInit->Ready */
380 AutoInitSpan autoInitSpan(this);
381 AssertReturn(autoInitSpan.isOk(), E_FAIL);
382
383 /* Weak reference to a VirtualBox object */
384 unconst(mVirtualBox) = aVirtualBox;
385
386 // initialize data
387 m = new Data;
388
389 /* Confirm a successful initialization */
390 autoInitSpan.setSucceeded();
391
392 return S_OK;
393}
394
395/**
396 * Appliance COM uninitializer.
397 * @return
398 */
399void Appliance::uninit()
400{
401 delete m;
402 m = NULL;
403}
404
405////////////////////////////////////////////////////////////////////////////////
406//
407// Appliance private methods
408//
409////////////////////////////////////////////////////////////////////////////////
410
411/**
412 * Private helper method that goes thru the elements of the given "current" element in the OVF XML
413 * and handles the contained child elements (which can be "Section" or "Content" elements).
414 *
415 * @param pcszPath Path spec of the XML file, for error messages.
416 * @param pReferencesElement "References" element from OVF, for looking up file specifications; can be NULL if no such element is present.
417 * @param pCurElem Element whose children are to be analyzed here.
418 * @return
419 */
420HRESULT Appliance::LoopThruSections(const char *pcszPath,
421 const xml::ElementNode *pReferencesElem,
422 const xml::ElementNode *pCurElem)
423{
424 HRESULT rc;
425
426 xml::NodesLoop loopChildren(*pCurElem);
427 const xml::ElementNode *pElem;
428 while ((pElem = loopChildren.forAllNodes()))
429 {
430 const char *pcszElemName = pElem->getName();
431 const char *pcszTypeAttr = "";
432 const xml::AttributeNode *pTypeAttr;
433 if ((pTypeAttr = pElem->findAttribute("type")))
434 pcszTypeAttr = pTypeAttr->getValue();
435
436 if ( (!strcmp(pcszElemName, "DiskSection"))
437 || ( (!strcmp(pcszElemName, "Section"))
438 && (!strcmp(pcszTypeAttr, "ovf:DiskSection_Type"))
439 )
440 )
441 {
442 if (!(SUCCEEDED((rc = HandleDiskSection(pcszPath, pReferencesElem, pElem)))))
443 return rc;
444 }
445 else if ( (!strcmp(pcszElemName, "NetworkSection"))
446 || ( (!strcmp(pcszElemName, "Section"))
447 && (!strcmp(pcszTypeAttr, "ovf:NetworkSection_Type"))
448 )
449 )
450 {
451 if (!(SUCCEEDED((rc = HandleNetworkSection(pcszPath, pElem)))))
452 return rc;
453 }
454 else if ( (!strcmp(pcszElemName, "DeploymentOptionSection")))
455 {
456 // TODO
457 }
458 else if ( (!strcmp(pcszElemName, "Info")))
459 {
460 // child of VirtualSystemCollection -- TODO
461 }
462 else if ( (!strcmp(pcszElemName, "ResourceAllocationSection")))
463 {
464 // child of VirtualSystemCollection -- TODO
465 }
466 else if ( (!strcmp(pcszElemName, "StartupSection")))
467 {
468 // child of VirtualSystemCollection -- TODO
469 }
470 else if ( (!strcmp(pcszElemName, "VirtualSystem"))
471 || ( (!strcmp(pcszElemName, "Content"))
472 && (!strcmp(pcszTypeAttr, "ovf:VirtualSystem_Type"))
473 )
474 )
475 {
476 if (!(SUCCEEDED((rc = HandleVirtualSystemContent(pcszPath, pElem)))))
477 return rc;
478 }
479 else if ( (!strcmp(pcszElemName, "VirtualSystemCollection"))
480 || ( (!strcmp(pcszElemName, "Content"))
481 && (!strcmp(pcszTypeAttr, "ovf:VirtualSystemCollection_Type"))
482 )
483 )
484 {
485 // TODO ResourceAllocationSection
486
487 // recurse for this, since it has VirtualSystem elements as children
488 if (!(SUCCEEDED((rc = LoopThruSections(pcszPath, pReferencesElem, pElem)))))
489 return rc;
490 }
491 }
492
493 return S_OK;
494}
495
496/**
497 * Private helper method that handles disk sections in the OVF XML.
498 * Gets called indirectly from IAppliance::read().
499 *
500 * @param pcszPath Path spec of the XML file, for error messages.
501 * @param pReferencesElement "References" element from OVF, for looking up file specifications; can be NULL if no such element is present.
502 * @param pSectionElem Section element for which this helper is getting called.
503 * @return
504 */
505HRESULT Appliance::HandleDiskSection(const char *pcszPath,
506 const xml::ElementNode *pReferencesElem,
507 const xml::ElementNode *pSectionElem)
508{
509 // contains "Disk" child elements
510 xml::NodesLoop loopDisks(*pSectionElem, "Disk");
511 const xml::ElementNode *pelmDisk;
512 while ((pelmDisk = loopDisks.forAllNodes()))
513 {
514 DiskImage d;
515 const char *pcszBad = NULL;
516 if (!(pelmDisk->getAttributeValue("diskId", d.strDiskId)))
517 pcszBad = "diskId";
518 else if (!(pelmDisk->getAttributeValue("format", d.strFormat)))
519 pcszBad = "format";
520 else if (!(pelmDisk->getAttributeValue("capacity", d.iCapacity)))
521 pcszBad = "capacity";
522 else
523 {
524 if (!(pelmDisk->getAttributeValue("populatedSize", d.iPopulatedSize)))
525 // optional
526 d.iPopulatedSize = -1;
527
528 Utf8Str strFileRef;
529 if (pelmDisk->getAttributeValue("fileRef", strFileRef)) // optional
530 {
531 // look up corresponding /References/File nodes (list built above)
532 const xml::ElementNode *pFileElem;
533 if ( pReferencesElem
534 && ((pFileElem = pReferencesElem->findChildElementFromId(strFileRef.c_str())))
535 )
536 {
537 // copy remaining values from file node then
538 const char *pcszBadInFile = NULL;
539 if (!(pFileElem->getAttributeValue("href", d.strHref)))
540 pcszBadInFile = "href";
541 else if (!(pFileElem->getAttributeValue("size", d.iSize)))
542 d.iSize = -1; // optional
543 // if (!(pFileElem->getAttributeValue("size", d.iChunkSize))) TODO
544 d.iChunkSize = -1; // optional
545 pFileElem->getAttributeValue("compression", d.strCompression);
546
547 if (pcszBadInFile)
548 return setError(VBOX_E_FILE_ERROR,
549 tr("Error reading \"%s\": missing or invalid attribute '%s' in 'File' element, line %d"),
550 pcszPath,
551 pcszBadInFile,
552 pFileElem->getLineNumber());
553 }
554 else
555 return setError(VBOX_E_FILE_ERROR,
556 tr("Error reading \"%s\": cannot find References/File element for ID '%s' referenced by 'Disk' element, line %d"),
557 pcszPath,
558 strFileRef.c_str(),
559 pelmDisk->getLineNumber());
560 }
561 }
562
563 if (pcszBad)
564 return setError(VBOX_E_FILE_ERROR,
565 tr("Error reading \"%s\": missing or invalid attribute '%s' in 'DiskSection' element, line %d"),
566 pcszPath,
567 pcszBad,
568 pelmDisk->getLineNumber());
569
570 m->mapDisks[d.strDiskId] = d;
571 }
572
573 return S_OK;
574}
575
576/**
577 * Private helper method that handles network sections in the OVF XML.
578 * Gets called indirectly from IAppliance::read().
579 *
580 * @param pcszPath Path spec of the XML file, for error messages.
581 * @param pSectionElem Section element for which this helper is getting called.
582 * @return
583 */
584HRESULT Appliance::HandleNetworkSection(const char * /* pcszPath */,
585 const xml::ElementNode * /* pSectionElem */)
586{
587 // we ignore network sections for now
588
589// xml::NodesLoop loopNetworks(*pSectionElem, "Network");
590// const xml::Node *pelmNetwork;
591// while ((pelmNetwork = loopNetworks.forAllNodes()))
592// {
593// Network n;
594// if (!(pelmNetwork->getAttributeValue("name", n.strNetworkName)))
595// return setError(VBOX_E_FILE_ERROR,
596// tr("Error reading \"%s\": missing 'name' attribute in 'Network', line %d"),
597// pcszPath,
598// pelmNetwork->getLineNumber());
599//
600// m->mapNetworks[n.strNetworkName] = n;
601// }
602
603 return S_OK;
604}
605
606/**
607 * Private helper method that handles a "VirtualSystem" element in the OVF XML.
608 * Gets called indirectly from IAppliance::read().
609 *
610 * @param pcszPath
611 * @param pContentElem
612 * @return
613 */
614HRESULT Appliance::HandleVirtualSystemContent(const char *pcszPath,
615 const xml::ElementNode *pelmVirtualSystem)
616{
617 VirtualSystem vsys;
618
619 const xml::AttributeNode *pIdAttr = pelmVirtualSystem->findAttribute("id");
620 if (pIdAttr)
621 vsys.strName = pIdAttr->getValue();
622
623 xml::NodesLoop loop(*pelmVirtualSystem); // all child elements
624 const xml::ElementNode *pelmThis;
625 while ((pelmThis = loop.forAllNodes()))
626 {
627 const char *pcszElemName = pelmThis->getName();
628 const xml::AttributeNode *pTypeAttr = pelmThis->findAttribute("type");
629 const char *pcszTypeAttr = (pTypeAttr) ? pTypeAttr->getValue() : "";
630
631 if ( (!strcmp(pcszElemName, "EulaSection"))
632 || (!strcmp(pcszTypeAttr, "ovf:EulaSection_Type"))
633 )
634 {
635 /* <EulaSection>
636 <Info ovf:msgid="6">License agreement for the Virtual System.</Info>
637 <License ovf:msgid="1">License terms can go in here.</License>
638 </EulaSection> */
639
640 const xml::ElementNode *pelmInfo, *pelmLicense;
641 if ( ((pelmInfo = pelmThis->findChildElement("Info")))
642 && ((pelmLicense = pelmThis->findChildElement("License")))
643 )
644 {
645 vsys.strLicenseInfo = pelmInfo->getValue();
646 vsys.strLicenseText = pelmLicense->getValue();
647 }
648 }
649 else if ( (!strcmp(pcszElemName, "VirtualHardwareSection"))
650 || (!strcmp(pcszTypeAttr, "ovf:VirtualHardwareSection_Type"))
651 )
652 {
653 const xml::ElementNode *pelmSystem, *pelmVirtualSystemType;
654 if ((pelmSystem = pelmThis->findChildElement("System")))
655 {
656 /* <System>
657 <vssd:Description>Description of the virtual hardware section.</vssd:Description>
658 <vssd:ElementName>vmware</vssd:ElementName>
659 <vssd:InstanceID>1</vssd:InstanceID>
660 <vssd:VirtualSystemIdentifier>MyLampService</vssd:VirtualSystemIdentifier>
661 <vssd:VirtualSystemType>vmx-4</vssd:VirtualSystemType>
662 </System>*/
663 if ((pelmVirtualSystemType = pelmSystem->findChildElement("VirtualSystemType")))
664 vsys.strVirtualSystemType = pelmVirtualSystemType->getValue();
665 }
666
667 xml::NodesLoop loopVirtualHardwareItems(*pelmThis, "Item"); // all "Item" child elements
668 const xml::ElementNode *pelmItem;
669 while ((pelmItem = loopVirtualHardwareItems.forAllNodes()))
670 {
671 VirtualHardwareItem i;
672
673 i.ulLineNumber = pelmItem->getLineNumber();
674
675 xml::NodesLoop loopItemChildren(*pelmItem); // all child elements
676 const xml::ElementNode *pelmItemChild;
677 while ((pelmItemChild = loopItemChildren.forAllNodes()))
678 {
679 const char *pcszItemChildName = pelmItemChild->getName();
680 if (!strcmp(pcszItemChildName, "Description"))
681 i.strDescription = pelmItemChild->getValue();
682 else if (!strcmp(pcszItemChildName, "Caption"))
683 i.strCaption = pelmItemChild->getValue();
684 else if (!strcmp(pcszItemChildName, "ElementName"))
685 i.strElementName = pelmItemChild->getValue();
686 else if ( (!strcmp(pcszItemChildName, "InstanceID"))
687 || (!strcmp(pcszItemChildName, "InstanceId"))
688 )
689 pelmItemChild->copyValue(i.ulInstanceID);
690 else if (!strcmp(pcszItemChildName, "HostResource"))
691 i.strHostResource = pelmItemChild->getValue();
692 else if (!strcmp(pcszItemChildName, "ResourceType"))
693 {
694 uint32_t ulType;
695 pelmItemChild->copyValue(ulType);
696 i.resourceType = (OVFResourceType_T)ulType;
697 }
698 else if (!strcmp(pcszItemChildName, "OtherResourceType"))
699 i.strOtherResourceType = pelmItemChild->getValue();
700 else if (!strcmp(pcszItemChildName, "ResourceSubType"))
701 i.strResourceSubType = pelmItemChild->getValue();
702 else if (!strcmp(pcszItemChildName, "AutomaticAllocation"))
703 i.fAutomaticAllocation = (!strcmp(pelmItemChild->getValue(), "true")) ? true : false;
704 else if (!strcmp(pcszItemChildName, "AutomaticDeallocation"))
705 i.fAutomaticDeallocation = (!strcmp(pelmItemChild->getValue(), "true")) ? true : false;
706 else if (!strcmp(pcszItemChildName, "Parent"))
707 pelmItemChild->copyValue(i.ulParent);
708 else if (!strcmp(pcszItemChildName, "Connection"))
709 i.strConnection = pelmItemChild->getValue();
710 else if (!strcmp(pcszItemChildName, "Address"))
711 i.strAddress = pelmItemChild->getValue();
712 else if (!strcmp(pcszItemChildName, "AddressOnParent"))
713 i.strAddressOnParent = pelmItemChild->getValue();
714 else if (!strcmp(pcszItemChildName, "AllocationUnits"))
715 i.strAllocationUnits = pelmItemChild->getValue();
716 else if (!strcmp(pcszItemChildName, "VirtualQuantity"))
717 pelmItemChild->copyValue(i.ullVirtualQuantity);
718 else if (!strcmp(pcszItemChildName, "Reservation"))
719 pelmItemChild->copyValue(i.ullReservation);
720 else if (!strcmp(pcszItemChildName, "Limit"))
721 pelmItemChild->copyValue(i.ullLimit);
722 else if (!strcmp(pcszItemChildName, "Weight"))
723 pelmItemChild->copyValue(i.ullWeight);
724 else if (!strcmp(pcszItemChildName, "ConsumerVisibility"))
725 i.strConsumerVisibility = pelmItemChild->getValue();
726 else if (!strcmp(pcszItemChildName, "MappingBehavior"))
727 i.strMappingBehavior = pelmItemChild->getValue();
728 else if (!strcmp(pcszItemChildName, "PoolID"))
729 i.strPoolID = pelmItemChild->getValue();
730 else if (!strcmp(pcszItemChildName, "BusNumber"))
731 pelmItemChild->copyValue(i.ulBusNumber);
732 else
733 return setError(VBOX_E_FILE_ERROR,
734 tr("Error reading \"%s\": unknown element \"%s\" under Item element, line %d"),
735 pcszPath,
736 pcszItemChildName,
737 i.ulLineNumber);
738 }
739
740 // store!
741 vsys.mapHardwareItems[i.ulInstanceID] = i;
742 }
743
744 // now go thru all hardware items and handle them according to their type;
745 // in this first loop we handle all items _except_ hard disk images,
746 // which we'll handle in a second loop below
747 HardwareItemsMap::const_iterator itH;
748 for (itH = vsys.mapHardwareItems.begin();
749 itH != vsys.mapHardwareItems.end();
750 ++itH)
751 {
752 const VirtualHardwareItem &i = itH->second;
753
754 // do some analysis
755 switch (i.resourceType)
756 {
757 case OVFResourceType_Processor: // 3
758 /* <rasd:Caption>1 virtual CPU</rasd:Caption>
759 <rasd:Description>Number of virtual CPUs</rasd:Description>
760 <rasd:ElementName>virtual CPU</rasd:ElementName>
761 <rasd:InstanceID>1</rasd:InstanceID>
762 <rasd:ResourceType>3</rasd:ResourceType>
763 <rasd:VirtualQuantity>1</rasd:VirtualQuantity>*/
764 if (i.ullVirtualQuantity < UINT16_MAX)
765 vsys.cCPUs = (uint16_t)i.ullVirtualQuantity;
766 else
767 return setError(VBOX_E_FILE_ERROR,
768 tr("Error reading \"%s\": CPU count %RI64 is larger than %d, line %d"),
769 pcszPath,
770 i.ullVirtualQuantity,
771 UINT16_MAX,
772 i.ulLineNumber);
773 break;
774
775 case OVFResourceType_Memory: // 4
776 if ( (i.strAllocationUnits == "MegaBytes") // found in OVF created by OVF toolkit
777 || (i.strAllocationUnits == "MB") // found in MS docs
778 || (i.strAllocationUnits == "byte * 2^20") // suggested by OVF spec DSP0243 page 21
779 )
780 vsys.ullMemorySize = i.ullVirtualQuantity * 1024 * 1024;
781 else
782 return setError(VBOX_E_FILE_ERROR,
783 tr("Error reading \"%s\": Invalid allocation unit \"%s\" specified with memory size item, line %d"),
784 pcszPath,
785 i.strAllocationUnits.c_str(),
786 i.ulLineNumber);
787 break;
788
789 case OVFResourceType_IDEController: // 5
790 {
791 /* <Item>
792 <rasd:Caption>ideController0</rasd:Caption>
793 <rasd:Description>IDE Controller</rasd:Description>
794 <rasd:InstanceId>5</rasd:InstanceId>
795 <rasd:ResourceType>5</rasd:ResourceType>
796 <rasd:Address>0</rasd:Address>
797 <rasd:BusNumber>0</rasd:BusNumber>
798 </Item> */
799 HardDiskController hdc;
800 hdc.system = HardDiskController::IDE;
801 hdc.idController = i.ulInstanceID;
802 hdc.strControllerType = i.strResourceSubType;
803 hdc.strAddress = i.strAddress;
804 hdc.ulBusNumber = i.ulBusNumber;
805
806 vsys.mapControllers[i.ulInstanceID] = hdc;
807 }
808 break;
809
810 case OVFResourceType_ParallelSCSIHBA: // 6 SCSI controller
811 {
812 /* <Item>
813 <rasd:Caption>SCSI Controller 0 - LSI Logic</rasd:Caption>
814 <rasd:Description>SCI Controller</rasd:Description>
815 <rasd:ElementName>SCSI controller</rasd:ElementName>
816 <rasd:InstanceID>4</rasd:InstanceID>
817 <rasd:ResourceSubType>LsiLogic</rasd:ResourceSubType>
818 <rasd:ResourceType>6</rasd:ResourceType>
819 </Item> */
820 HardDiskController hdc;
821 hdc.system = HardDiskController::SCSI;
822 hdc.idController = i.ulInstanceID;
823 hdc.strControllerType = i.strResourceSubType;
824
825 vsys.mapControllers[i.ulInstanceID] = hdc;
826 }
827 break;
828
829 case OVFResourceType_EthernetAdapter: // 10
830 {
831 /* <Item>
832 <rasd:Caption>Ethernet adapter on 'Bridged'</rasd:Caption>
833 <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
834 <rasd:Connection>Bridged</rasd:Connection>
835 <rasd:InstanceID>6</rasd:InstanceID>
836 <rasd:ResourceType>10</rasd:ResourceType>
837 <rasd:ResourceSubType>E1000</rasd:ResourceSubType>
838 </Item>
839
840 OVF spec DSP 0243 page 21:
841 "For an Ethernet adapter, this specifies the abstract network connection name
842 for the virtual machine. All Ethernet adapters that specify the same abstract
843 network connection name within an OVF package shall be deployed on the same
844 network. The abstract network connection name shall be listed in the NetworkSection
845 at the outermost envelope level." */
846
847 // only store the name
848 EthernetAdapter ea;
849 ea.strAdapterType = i.strResourceSubType;
850 ea.strNetworkName = i.strConnection;
851 vsys.llEthernetAdapters.push_back(ea);
852 }
853 break;
854
855 case OVFResourceType_FloppyDrive: // 14
856 vsys.fHasFloppyDrive = true; // we have no additional information
857 break;
858
859 case OVFResourceType_CDDrive: // 15
860 /* <Item ovf:required="false">
861 <rasd:Caption>cdrom1</rasd:Caption>
862 <rasd:InstanceId>7</rasd:InstanceId>
863 <rasd:ResourceType>15</rasd:ResourceType>
864 <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
865 <rasd:Parent>5</rasd:Parent>
866 <rasd:AddressOnParent>0</rasd:AddressOnParent>
867 </Item> */
868 // I tried to see what happens if I set an ISO for the CD-ROM in VMware Workstation,
869 // but then the ovftool dies with "Device backing not supported". So I guess if
870 // VMware can't export ISOs, then we don't need to be able to import them right now.
871 vsys.fHasCdromDrive = true; // we have no additional information
872 break;
873
874 case OVFResourceType_HardDisk: // 17
875 // handled separately in second loop below
876 break;
877
878 case OVFResourceType_OtherStorageDevice: // 20 SATA controller
879 {
880 /* <Item>
881 <rasd:Description>SATA Controller</rasd:Description>
882 <rasd:Caption>sataController0</rasd:Caption>
883 <rasd:InstanceID>4</rasd:InstanceID>
884 <rasd:ResourceType>20</rasd:ResourceType>
885 <rasd:ResourceSubType>AHCI</rasd:ResourceSubType>
886 <rasd:Address>0</rasd:Address>
887 <rasd:BusNumber>0</rasd:BusNumber>
888 </Item> */
889 if (i.strCaption.startsWith ("sataController", Utf8Str::CaseInsensitive) &&
890 !i.strResourceSubType.compare ("AHCI", Utf8Str::CaseInsensitive))
891 {
892 HardDiskController hdc;
893 hdc.system = HardDiskController::SATA;
894 hdc.idController = i.ulInstanceID;
895 hdc.strControllerType = i.strResourceSubType;
896
897 vsys.mapControllers[i.ulInstanceID] = hdc;
898 }
899 else
900 return setError(VBOX_E_FILE_ERROR,
901 tr("Error reading \"%s\": Host resource of type \"Other Storage Device (%d)\" is supported with SATA AHCI controllers only, line %d"),
902 pcszPath,
903 OVFResourceType_OtherStorageDevice,
904 i.ulLineNumber);
905 }
906 break;
907
908 case OVFResourceType_USBController: // 23
909 /* <Item ovf:required="false">
910 <rasd:Caption>usb</rasd:Caption>
911 <rasd:Description>USB Controller</rasd:Description>
912 <rasd:InstanceId>3</rasd:InstanceId>
913 <rasd:ResourceType>23</rasd:ResourceType>
914 <rasd:Address>0</rasd:Address>
915 <rasd:BusNumber>0</rasd:BusNumber>
916 </Item> */
917 vsys.fHasUsbController = true; // we have no additional information
918 break;
919
920 case OVFResourceType_SoundCard: // 35
921 /* <Item ovf:required="false">
922 <rasd:Caption>sound</rasd:Caption>
923 <rasd:Description>Sound Card</rasd:Description>
924 <rasd:InstanceId>10</rasd:InstanceId>
925 <rasd:ResourceType>35</rasd:ResourceType>
926 <rasd:ResourceSubType>ensoniq1371</rasd:ResourceSubType>
927 <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
928 <rasd:AddressOnParent>3</rasd:AddressOnParent>
929 </Item> */
930 vsys.strSoundCardType = i.strResourceSubType;
931 break;
932
933 default:
934 return setError(VBOX_E_FILE_ERROR,
935 tr("Error reading \"%s\": Unknown resource type %d in hardware item, line %d"),
936 pcszPath,
937 i.resourceType,
938 i.ulLineNumber);
939 } // end switch
940 }
941
942 // now run through the items for a second time, but handle only
943 // hard disk images; otherwise the code would fail if a hard
944 // disk image appears in the OVF before its hard disk controller
945 for (itH = vsys.mapHardwareItems.begin();
946 itH != vsys.mapHardwareItems.end();
947 ++itH)
948 {
949 const VirtualHardwareItem &i = itH->second;
950
951 // do some analysis
952 switch (i.resourceType)
953 {
954 case OVFResourceType_HardDisk: // 17
955 {
956 /* <Item>
957 <rasd:Caption>Harddisk 1</rasd:Caption>
958 <rasd:Description>HD</rasd:Description>
959 <rasd:ElementName>Hard Disk</rasd:ElementName>
960 <rasd:HostResource>ovf://disk/lamp</rasd:HostResource>
961 <rasd:InstanceID>5</rasd:InstanceID>
962 <rasd:Parent>4</rasd:Parent>
963 <rasd:ResourceType>17</rasd:ResourceType>
964 </Item> */
965
966 // look up the hard disk controller element whose InstanceID equals our Parent;
967 // this is how the connection is specified in OVF
968 ControllersMap::const_iterator it = vsys.mapControllers.find(i.ulParent);
969 if (it == vsys.mapControllers.end())
970 return setError(VBOX_E_FILE_ERROR,
971 tr("Error reading \"%s\": Hard disk item with instance ID %d specifies invalid parent %d, line %d"),
972 pcszPath,
973 i.ulInstanceID,
974 i.ulParent,
975 i.ulLineNumber);
976 //const HardDiskController &hdc = it->second;
977
978 VirtualDisk vd;
979 vd.idController = i.ulParent;
980 i.strAddressOnParent.toInt(vd.ulAddressOnParent);
981 // ovf://disk/lamp
982 // 123456789012345
983 if (i.strHostResource.substr(0, 11) == "ovf://disk/")
984 vd.strDiskId = i.strHostResource.substr(11);
985 else if (i.strHostResource.substr(0, 6) == "/disk/")
986 vd.strDiskId = i.strHostResource.substr(6);
987
988 if ( !(vd.strDiskId.length())
989 || (m->mapDisks.find(vd.strDiskId) == m->mapDisks.end())
990 )
991 return setError(VBOX_E_FILE_ERROR,
992 tr("Error reading \"%s\": Hard disk item with instance ID %d specifies invalid host resource \"%s\", line %d"),
993 pcszPath,
994 i.ulInstanceID,
995 i.strHostResource.c_str(),
996 i.ulLineNumber);
997
998 vsys.mapVirtualDisks[vd.strDiskId] = vd;
999 }
1000 break;
1001 }
1002 }
1003 }
1004 else if ( (!strcmp(pcszElemName, "OperatingSystemSection"))
1005 || (!strcmp(pcszTypeAttr, "ovf:OperatingSystemSection_Type"))
1006 )
1007 {
1008 uint64_t cimos64;
1009 if (!(pelmThis->getAttributeValue("id", cimos64)))
1010 return setError(VBOX_E_FILE_ERROR,
1011 tr("Error reading \"%s\": missing or invalid 'ovf:id' attribute in operating system section element, line %d"),
1012 pcszPath,
1013 pelmThis->getLineNumber());
1014
1015 vsys.cimos = (CIMOSType_T)cimos64;
1016 }
1017 else if ( (!strcmp(pcszElemName, "AnnotationSection"))
1018 || (!strcmp(pcszTypeAttr, "ovf:AnnotationSection_Type"))
1019 )
1020 {
1021 const xml::ElementNode *pelmAnnotation;
1022 if ((pelmAnnotation = pelmThis->findChildElement("Annotation")))
1023 vsys.strDescription = pelmAnnotation->getValue();
1024 }
1025 }
1026
1027 // now create the virtual system
1028 m->llVirtualSystems.push_back(vsys);
1029
1030 return S_OK;
1031}
1032
1033////////////////////////////////////////////////////////////////////////////////
1034//
1035// IAppliance public methods
1036//
1037////////////////////////////////////////////////////////////////////////////////
1038
1039/**
1040 * Public method implementation.
1041 * @param
1042 * @return
1043 */
1044STDMETHODIMP Appliance::COMGETTER(Path)(BSTR *aPath)
1045{
1046 if (!aPath)
1047 return E_POINTER;
1048
1049 AutoCaller autoCaller(this);
1050 CheckComRCReturnRC(autoCaller.rc());
1051
1052 AutoReadLock alock(this);
1053
1054 Bstr bstrPath(m->strPath);
1055 bstrPath.cloneTo(aPath);
1056
1057 return S_OK;
1058}
1059
1060/**
1061 * Public method implementation.
1062 * @param
1063 * @return
1064 */
1065STDMETHODIMP Appliance::COMGETTER(Disks)(ComSafeArrayOut(BSTR, aDisks))
1066{
1067 CheckComArgOutSafeArrayPointerValid(aDisks);
1068
1069 AutoCaller autoCaller(this);
1070 CheckComRCReturnRC(autoCaller.rc());
1071
1072 AutoReadLock alock(this);
1073
1074 size_t c = m->mapDisks.size();
1075 com::SafeArray<BSTR> sfaDisks(c);
1076
1077 DiskImagesMap::const_iterator it;
1078 size_t i = 0;
1079 for (it = m->mapDisks.begin();
1080 it != m->mapDisks.end();
1081 ++it, ++i)
1082 {
1083 // create a string representing this disk
1084 const DiskImage &d = it->second;
1085 char *psz = NULL;
1086 RTStrAPrintf(&psz,
1087 "%s\t"
1088 "%RI64\t"
1089 "%RI64\t"
1090 "%s\t"
1091 "%s\t"
1092 "%RI64\t"
1093 "%RI64\t"
1094 "%s",
1095 d.strDiskId.c_str(),
1096 d.iCapacity,
1097 d.iPopulatedSize,
1098 d.strFormat.c_str(),
1099 d.strHref.c_str(),
1100 d.iSize,
1101 d.iChunkSize,
1102 d.strCompression.c_str());
1103 Utf8Str utf(psz);
1104 Bstr bstr(utf);
1105 // push to safearray
1106 bstr.cloneTo(&sfaDisks[i]);
1107 RTStrFree(psz);
1108 }
1109
1110 sfaDisks.detachTo(ComSafeArrayOutArg(aDisks));
1111
1112 return S_OK;
1113}
1114
1115/**
1116 * Public method implementation.
1117 * @param
1118 * @return
1119 */
1120STDMETHODIMP Appliance::COMGETTER(VirtualSystemDescriptions)(ComSafeArrayOut(IVirtualSystemDescription*, aVirtualSystemDescriptions))
1121{
1122 CheckComArgOutSafeArrayPointerValid(aVirtualSystemDescriptions);
1123
1124 AutoCaller autoCaller(this);
1125 CheckComRCReturnRC(autoCaller.rc());
1126
1127 AutoReadLock alock(this);
1128
1129 SafeIfaceArray<IVirtualSystemDescription> sfaVSD(m->virtualSystemDescriptions);
1130 sfaVSD.detachTo(ComSafeArrayOutArg(aVirtualSystemDescriptions));
1131
1132 return S_OK;
1133}
1134
1135/**
1136 * Public method implementation.
1137 * @param path
1138 * @return
1139 */
1140STDMETHODIMP Appliance::Read(IN_BSTR path)
1141{
1142 HRESULT rc = S_OK;
1143
1144 if (!path)
1145 return E_POINTER;
1146
1147 AutoCaller autoCaller(this);
1148 CheckComRCReturnRC(autoCaller.rc());
1149
1150 AutoWriteLock alock(this);
1151
1152 // see if we can handle this file; for now we insist it has an ".ovf" extension
1153 m->strPath = path;
1154 if (!m->strPath.endsWith(".ovf", Utf8Str::CaseInsensitive))
1155 return setError(VBOX_E_FILE_ERROR,
1156 tr("Appliance file must have .ovf extension"));
1157
1158 try
1159 {
1160 xml::XmlFileParser parser;
1161 xml::Document doc;
1162 parser.read(m->strPath.raw(),
1163 doc);
1164
1165 const xml::ElementNode *pRootElem = doc.getRootElement();
1166 if (strcmp(pRootElem->getName(), "Envelope"))
1167 return setError(VBOX_E_FILE_ERROR,
1168 tr("Root element in OVF file must be \"Envelope\"."));
1169
1170 // OVF has the following rough layout:
1171 /*
1172 -- <References> .... files referenced from other parts of the file, such as VMDK images
1173 -- Metadata, comprised of several section commands
1174 -- virtual machines, either a single <VirtualSystem>, or a <VirtualSystemCollection>
1175 -- optionally <Strings> for localization
1176 */
1177
1178 // get all "File" child elements of "References" section so we can look up files easily;
1179 // first find the "References" sections so we can look up files
1180 xml::ElementNodesList listFileElements; // receives all /Envelope/References/File nodes
1181 const xml::ElementNode *pReferencesElem;
1182 if ((pReferencesElem = pRootElem->findChildElement("References")))
1183 pReferencesElem->getChildElements(listFileElements, "File");
1184
1185 // now go though the sections
1186 if (!(SUCCEEDED(rc = LoopThruSections(m->strPath.raw(), pReferencesElem, pRootElem))))
1187 return rc;
1188 }
1189 catch(xml::Error &x)
1190 {
1191 return setError(VBOX_E_FILE_ERROR,
1192 x.what());
1193 }
1194
1195 return S_OK;
1196}
1197
1198/**
1199 * Public method implementation.
1200 * @return
1201 */
1202STDMETHODIMP Appliance::Interpret()
1203{
1204 // @todo:
1205 // - don't use COM methods but the methods directly (faster, but needs appropriate locking of that objects itself (s. HardDisk))
1206 // - Appropriate handle errors like not supported file formats
1207 AutoCaller autoCaller(this);
1208 CheckComRCReturnRC(autoCaller.rc());
1209
1210 AutoWriteLock(this);
1211
1212 HRESULT rc = S_OK;
1213
1214 /* Clear any previous virtual system descriptions */
1215 m->virtualSystemDescriptions.clear();
1216
1217 /* We need the default path for storing disk images */
1218 ComPtr<ISystemProperties> systemProps;
1219 rc = mVirtualBox->COMGETTER(SystemProperties)(systemProps.asOutParam());
1220 CheckComRCReturnRC(rc);
1221 Bstr bstrDefaultHardDiskLocation;
1222 rc = systemProps->COMGETTER(DefaultHardDiskFolder)(bstrDefaultHardDiskLocation.asOutParam());
1223 CheckComRCReturnRC(rc);
1224
1225 /* Try/catch so we can clean up on error */
1226 try
1227 {
1228 list<VirtualSystem>::const_iterator it;
1229 /* Iterate through all virtual systems */
1230 for (it = m->llVirtualSystems.begin();
1231 it != m->llVirtualSystems.end();
1232 ++it)
1233 {
1234 const VirtualSystem &vsysThis = *it;
1235
1236 ComObjPtr<VirtualSystemDescription> pNewDesc;
1237 rc = pNewDesc.createObject();
1238 CheckComRCThrowRC(rc);
1239 rc = pNewDesc->init();
1240 CheckComRCThrowRC(rc);
1241
1242 /* Guest OS type */
1243 Utf8Str strOsTypeVBox,
1244 strCIMOSType = Utf8StrFmt("%RI32", (uint32_t)vsysThis.cimos);
1245 convertCIMOSType2VBoxOSType(strOsTypeVBox, vsysThis.cimos);
1246 pNewDesc->addEntry(VirtualSystemDescriptionType_OS,
1247 "",
1248 strCIMOSType,
1249 strOsTypeVBox);
1250
1251 /* VM name */
1252 /* If the there isn't any name specified create a default one out of
1253 * the OS type */
1254 Utf8Str nameVBox = vsysThis.strName;
1255 if (nameVBox.isEmpty())
1256 nameVBox = strOsTypeVBox;
1257 searchUniqueVMName(nameVBox);
1258 pNewDesc->addEntry(VirtualSystemDescriptionType_Name,
1259 "",
1260 vsysThis.strName,
1261 nameVBox);
1262
1263 /* VM description */
1264 if (!vsysThis.strDescription.isEmpty())
1265 pNewDesc->addEntry(VirtualSystemDescriptionType_Description,
1266 "",
1267 vsysThis.strDescription,
1268 vsysThis.strDescription);
1269
1270 /* VM license */
1271 if (!vsysThis.strLicenseText.isEmpty())
1272 pNewDesc->addEntry(VirtualSystemDescriptionType_License,
1273 "",
1274 vsysThis.strLicenseText,
1275 vsysThis.strLicenseText);
1276
1277 /* Now that we know the OS type, get our internal defaults based on that. */
1278 ComPtr<IGuestOSType> pGuestOSType;
1279 rc = mVirtualBox->GetGuestOSType(Bstr(strOsTypeVBox), pGuestOSType.asOutParam());
1280 CheckComRCThrowRC(rc);
1281
1282 /* CPU count */
1283 ULONG cpuCountVBox = vsysThis.cCPUs;
1284 /* Check for the constrains */
1285 if (cpuCountVBox > 1) //SchemaDefs::MaxCPUCount)
1286 {
1287 addWarning(tr("The virtual system \"%s\" claims support for %u CPU's, but VirtualBox has support for max %u CPU's only."),
1288 vsysThis.strName.c_str(), cpuCountVBox, 1); //SchemaDefs::MaxCPUCount);
1289 cpuCountVBox = 1; //SchemaDefs::MaxCPUCount;
1290 }
1291 if (vsysThis.cCPUs == 0)
1292 cpuCountVBox = 1;
1293 pNewDesc->addEntry(VirtualSystemDescriptionType_CPU,
1294 "",
1295 Utf8StrFmt("%RI32", (uint32_t)vsysThis.cCPUs),
1296 Utf8StrFmt("%RI32", (uint32_t)cpuCountVBox));
1297
1298 /* RAM */
1299 uint64_t ullMemSizeVBox = vsysThis.ullMemorySize / _1M;
1300 /* Check for the constrains */
1301 if (ullMemSizeVBox != 0 &&
1302 (ullMemSizeVBox < MM_RAM_MIN_IN_MB ||
1303 ullMemSizeVBox > MM_RAM_MAX_IN_MB))
1304 {
1305 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."),
1306 vsysThis.strName.c_str(), ullMemSizeVBox, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1307 ullMemSizeVBox = RT_MIN(RT_MAX(ullMemSizeVBox, MM_RAM_MIN_IN_MB), MM_RAM_MAX_IN_MB);
1308 }
1309 if (vsysThis.ullMemorySize == 0)
1310 {
1311 /* If the RAM of the OVF is zero, use our predefined values */
1312 ULONG memSizeVBox2;
1313 rc = pGuestOSType->COMGETTER(RecommendedRAM)(&memSizeVBox2);
1314 CheckComRCThrowRC(rc);
1315 /* VBox stores that in MByte */
1316 ullMemSizeVBox = (uint64_t)memSizeVBox2;
1317 }
1318 pNewDesc->addEntry(VirtualSystemDescriptionType_Memory,
1319 "",
1320 Utf8StrFmt("%RI64", (uint64_t)vsysThis.ullMemorySize),
1321 Utf8StrFmt("%RI64", (uint64_t)ullMemSizeVBox));
1322
1323 /* Audio */
1324 if (!vsysThis.strSoundCardType.isNull())
1325 /* Currently we set the AC97 always.
1326 @todo: figure out the hardware which could be possible */
1327 pNewDesc->addEntry(VirtualSystemDescriptionType_SoundCard,
1328 "",
1329 vsysThis.strSoundCardType,
1330 Utf8StrFmt("%RI32", (uint32_t)AudioControllerType_AC97));
1331
1332#ifdef VBOX_WITH_USB
1333 /* USB Controller */
1334 if (vsysThis.fHasUsbController)
1335 pNewDesc->addEntry(VirtualSystemDescriptionType_USBController, "", "", "");
1336#endif /* VBOX_WITH_USB */
1337
1338 /* Network Controller */
1339 uint32_t cEthernetAdapters = vsysThis.llEthernetAdapters.size();
1340 if (cEthernetAdapters > 0)
1341 {
1342 /* Check for the constrains */
1343 if (cEthernetAdapters > SchemaDefs::NetworkAdapterCount)
1344 addWarning(tr("The virtual system \"%s\" claims support for %u network adapters, but VirtualBox has support for max %u network adapter only."),
1345 vsysThis.strName.c_str(), cEthernetAdapters, SchemaDefs::NetworkAdapterCount);
1346
1347 /* Get the default network adapter type for the selected guest OS */
1348 NetworkAdapterType_T defaultAdapterVBox = NetworkAdapterType_Am79C970A;
1349 rc = pGuestOSType->COMGETTER(AdapterType)(&defaultAdapterVBox);
1350 CheckComRCThrowRC(rc);
1351
1352 EthernetAdaptersList::const_iterator itEA;
1353 /* Iterate through all abstract networks. We support 8 network
1354 * adapters at the maximum, so the first 8 will be added only. */
1355 size_t a = 0;
1356 for (itEA = vsysThis.llEthernetAdapters.begin();
1357 itEA != vsysThis.llEthernetAdapters.end() && a < SchemaDefs::NetworkAdapterCount;
1358 ++itEA, ++a)
1359 {
1360 const EthernetAdapter &ea = *itEA; // logical network to connect to
1361 Utf8Str strNetwork = ea.strNetworkName;
1362 // make sure it's one of these two
1363 if ( (strNetwork.compare("Null", Utf8Str::CaseInsensitive))
1364 && (strNetwork.compare("Bridged", Utf8Str::CaseInsensitive))
1365 && (strNetwork.compare("Internal", Utf8Str::CaseInsensitive))
1366 && (strNetwork.compare("HostOnly", Utf8Str::CaseInsensitive))
1367 )
1368 strNetwork = "Bridged"; // VMware assumes this is the default apparently
1369
1370 /* Figure out the hardware type */
1371 NetworkAdapterType_T nwAdapterVBox = defaultAdapterVBox;
1372 if (!ea.strAdapterType.compare("PCNet32", Utf8Str::CaseInsensitive))
1373 {
1374 /* If the default adapter is already one of the two
1375 * PCNet adapters use the default one. If not use the
1376 * Am79C970A as fallback. */
1377 if (!(defaultAdapterVBox == NetworkAdapterType_Am79C970A ||
1378 defaultAdapterVBox == NetworkAdapterType_Am79C973))
1379 nwAdapterVBox = NetworkAdapterType_Am79C970A;
1380 }
1381#ifdef VBOX_WITH_E1000
1382 else if (!ea.strAdapterType.compare("E1000", Utf8Str::CaseInsensitive))
1383 {
1384 /* If the default adapter is already one of the two
1385 * E1000 adapters use the default one. If not use the
1386 * I82540EM as fallback. */
1387 if (!(defaultAdapterVBox == NetworkAdapterType_I82540EM ||
1388 defaultAdapterVBox == NetworkAdapterType_I82543GC))
1389 nwAdapterVBox = NetworkAdapterType_I82540EM;
1390 }
1391#endif /* VBOX_WITH_E1000 */
1392
1393 pNewDesc->addEntry(VirtualSystemDescriptionType_NetworkAdapter,
1394 "", // ref
1395 ea.strNetworkName, // orig
1396 Utf8StrFmt("%RI32", (uint32_t)nwAdapterVBox), // conf
1397 0,
1398 Utf8StrFmt("type=%s", strNetwork.c_str())); // extra conf
1399 }
1400 }
1401
1402 /* Floppy Drive */
1403 if (vsysThis.fHasFloppyDrive)
1404 pNewDesc->addEntry(VirtualSystemDescriptionType_Floppy, "", "", "");
1405
1406 /* CD Drive */
1407 /* @todo: I can't disable the CDROM. So nothing to do for now */
1408 /*
1409 if (vsysThis.fHasCdromDrive)
1410 pNewDesc->addEntry(VirtualSystemDescriptionType_CDROM, "", "", "");*/
1411
1412 /* Hard disk Controller */
1413 uint16_t cIDEused = 0;
1414 uint16_t cSATAused = 0;
1415 uint16_t cSCSIused = 0;
1416 ControllersMap::const_iterator hdcIt;
1417 /* Iterate through all hard disk controllers */
1418 for (hdcIt = vsysThis.mapControllers.begin();
1419 hdcIt != vsysThis.mapControllers.end();
1420 ++hdcIt)
1421 {
1422 const HardDiskController &hdc = hdcIt->second;
1423 Utf8Str strControllerID = Utf8StrFmt("%RI32", (uint32_t)hdc.idController);
1424
1425 switch (hdc.system)
1426 {
1427 case HardDiskController::IDE:
1428 {
1429 /* Check for the constrains */
1430 /* @todo: I'm very confused! Are these bits *one* controller or
1431 is every port/bus declared as an extra controller. */
1432 if (cIDEused < 4)
1433 {
1434 // @todo: figure out the IDE types
1435 /* Use PIIX4 as default */
1436 Utf8Str strType = "PIIX4";
1437 if (!hdc.strControllerType.compare("PIIX3", Utf8Str::CaseInsensitive))
1438 strType = "PIIX3";
1439 else if (!hdc.strControllerType.compare("ICH6", Utf8Str::CaseInsensitive))
1440 strType = "ICH6";
1441 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerIDE,
1442 strControllerID,
1443 hdc.strControllerType,
1444 strType);
1445 }
1446 else
1447 {
1448 /* Warn only once */
1449 if (cIDEused == 1)
1450 addWarning(tr("The virtual \"%s\" system requests support for more than one IDE controller, but VirtualBox has support for only one."),
1451 vsysThis.strName.c_str());
1452
1453 }
1454 ++cIDEused;
1455 break;
1456 }
1457
1458#ifdef VBOX_WITH_AHCI
1459 case HardDiskController::SATA:
1460 {
1461 /* Check for the constrains */
1462 if (cSATAused < 1)
1463 {
1464 // @todo: figure out the SATA types
1465 /* We only support a plain AHCI controller, so use them always */
1466 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSATA,
1467 strControllerID,
1468 hdc.strControllerType,
1469 "AHCI");
1470 }
1471 else
1472 {
1473 /* Warn only once */
1474 if (cSATAused == 1)
1475 addWarning(tr("The virtual system \"%s\" requests support for more than one SATA controller, but VirtualBox has support for only one"),
1476 vsysThis.strName.c_str());
1477
1478 }
1479 ++cSATAused;
1480 break;
1481 }
1482#endif /* VBOX_WITH_AHCI */
1483
1484 case HardDiskController::SCSI:
1485 {
1486 /* Check for the constrains */
1487 if (cSCSIused < 1)
1488 {
1489 Utf8Str hdcController = "LsiLogic";
1490 if (!hdc.strControllerType.compare("BusLogic", Utf8Str::CaseInsensitive))
1491 hdcController = "BusLogic";
1492 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSCSI,
1493 strControllerID,
1494 hdc.strControllerType,
1495 hdcController);
1496 }
1497 else
1498 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."),
1499 vsysThis.strName.c_str(),
1500 hdc.strControllerType.c_str(),
1501 strControllerID.c_str());
1502 ++cSCSIused;
1503 break;
1504 }
1505 }
1506 }
1507
1508 /* Hard disks */
1509 if (vsysThis.mapVirtualDisks.size() > 0)
1510 {
1511 VirtualDisksMap::const_iterator itVD;
1512 /* Iterate through all hard disks ()*/
1513 for (itVD = vsysThis.mapVirtualDisks.begin();
1514 itVD != vsysThis.mapVirtualDisks.end();
1515 ++itVD)
1516 {
1517 const VirtualDisk &hd = itVD->second;
1518 /* Get the associated disk image */
1519 const DiskImage &di = m->mapDisks[hd.strDiskId];
1520
1521 // @todo:
1522 // - figure out all possible vmdk formats we also support
1523 // - figure out if there is a url specifier for vhd already
1524 // - we need a url specifier for the vdi format
1525 if ( di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#sparse", Utf8Str::CaseInsensitive)
1526 || di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#compressed", Utf8Str::CaseInsensitive))
1527 {
1528 /* If the href is empty use the VM name as filename */
1529 Utf8Str strFilename = di.strHref;
1530 if (!strFilename.length())
1531 strFilename = Utf8StrFmt("%s.vmdk", nameVBox.c_str());
1532 /* Construct a unique target path */
1533 Utf8StrFmt strPath("%ls%c%s",
1534 bstrDefaultHardDiskLocation.raw(),
1535 RTPATH_DELIMITER,
1536 strFilename.c_str());
1537 searchUniqueDiskImageFilePath(strPath);
1538
1539 /* find the description for the hard disk controller
1540 * that has the same ID as hd.idController */
1541 const VirtualSystemDescriptionEntry *pController;
1542 if (!(pController = pNewDesc->findControllerFromID(hd.idController)))
1543 throw setError(E_FAIL,
1544 tr("Cannot find hard disk controller with OVF instance ID %RI32 to which disk \"%s\" should be attached"),
1545 hd.idController,
1546 di.strHref.c_str());
1547
1548 /* controller to attach to, and the bus within that controller */
1549 Utf8StrFmt strExtraConfig("controller=%RI16;channel=%RI16",
1550 pController->ulIndex,
1551 hd.ulAddressOnParent);
1552 ULONG ulSize = 0;
1553 if (di.iPopulatedSize != -1)
1554 ulSize = (ULONG)(di.iPopulatedSize / _1M);
1555 else if (di.iSize != -1)
1556 ulSize = (ULONG)(di.iSize / _1M);
1557 else if (di.iCapacity != -1)
1558 ulSize = (ULONG)(di.iCapacity / _1M);
1559 if (ulSize == 0)
1560 ulSize = 10000; // assume 10 GB, this is for the progress bar only anyway
1561 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskImage,
1562 hd.strDiskId,
1563 di.strHref,
1564 strPath,
1565 ulSize,
1566 strExtraConfig);
1567 }
1568 else
1569 throw setError(VBOX_E_FILE_ERROR,
1570 tr("Unsupported format for virtual disk image in OVF: \"%s\"", di.strFormat.c_str()));
1571 }
1572 }
1573
1574 m->virtualSystemDescriptions.push_back(pNewDesc);
1575 }
1576 }
1577 catch (HRESULT aRC)
1578 {
1579 /* On error we clear the list & return */
1580 m->virtualSystemDescriptions.clear();
1581 rc = aRC;
1582 }
1583
1584 return rc;
1585}
1586
1587struct Appliance::TaskImportMachines
1588{
1589 TaskImportMachines(Appliance *aThat, Progress *aProgress)
1590 : pAppliance(aThat)
1591 , progress(aProgress)
1592 , rc(S_OK)
1593 {}
1594 ~TaskImportMachines() {}
1595
1596 HRESULT startThread();
1597
1598 Appliance *pAppliance;
1599 ComObjPtr<Progress> progress;
1600 HRESULT rc;
1601};
1602
1603HRESULT Appliance::TaskImportMachines::startThread()
1604{
1605 int vrc = RTThreadCreate(NULL, Appliance::taskThreadImportMachines, this,
1606 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
1607 "Appliance::Task");
1608 ComAssertMsgRCRet(vrc,
1609 ("Could not create taskThreadImportMachines (%Rrc)\n", vrc), E_FAIL);
1610
1611 return S_OK;
1612}
1613
1614/**
1615 * Public method implementation.
1616 * @param aProgress
1617 * @return
1618 */
1619STDMETHODIMP Appliance::ImportMachines(IProgress **aProgress)
1620{
1621 CheckComArgOutPointerValid(aProgress);
1622
1623 AutoCaller autoCaller(this);
1624 CheckComRCReturnRC(autoCaller.rc());
1625
1626 AutoReadLock(this);
1627
1628 HRESULT rc = S_OK;
1629
1630 ComObjPtr<Progress> progress;
1631 try
1632 {
1633 Bstr progressDesc = BstrFmt(tr("Import appliance '%s'"),
1634 m->strPath.raw());
1635 rc = setUpProgress(progress, progressDesc);
1636 if (FAILED(rc)) throw rc;
1637
1638 /* Initialize our worker task */
1639 std::auto_ptr<TaskImportMachines> task(new TaskImportMachines(this, progress));
1640 //AssertComRCThrowRC (task->autoCaller.rc());
1641
1642 rc = task->startThread();
1643 if (FAILED(rc)) throw rc;
1644
1645 task.release();
1646 }
1647 catch (HRESULT aRC)
1648 {
1649 rc = aRC;
1650 }
1651
1652 if (SUCCEEDED(rc))
1653 /* Return progress to the caller */
1654 progress.queryInterfaceTo(aProgress);
1655
1656 return rc;
1657}
1658
1659struct MyHardDiskAttachment
1660{
1661 Guid uuid;
1662 ComPtr<IMachine> pMachine;
1663 Bstr controllerType;
1664 int32_t lChannel;
1665 int32_t lDevice;
1666};
1667
1668/**
1669 * Worker thread implementation for ImportMachines().
1670 * @param aThread
1671 * @param pvUser
1672 */
1673/* static */
1674DECLCALLBACK(int) Appliance::taskThreadImportMachines(RTTHREAD /* aThread */, void *pvUser)
1675{
1676 std::auto_ptr<TaskImportMachines> task(static_cast<TaskImportMachines*>(pvUser));
1677 AssertReturn(task.get(), VERR_GENERAL_FAILURE);
1678
1679 Appliance *pAppliance = task->pAppliance;
1680
1681 LogFlowFuncEnter();
1682 LogFlowFunc(("Appliance %p\n", pAppliance));
1683
1684 AutoCaller autoCaller(pAppliance);
1685 CheckComRCReturnRC(autoCaller.rc());
1686
1687 AutoWriteLock appLock(pAppliance);
1688
1689 HRESULT rc = S_OK;
1690
1691 ComPtr<IVirtualBox> pVirtualBox(pAppliance->mVirtualBox);
1692
1693 // rollback for errors:
1694 // 1) a list of images that we created/imported
1695 list<MyHardDiskAttachment> llHardDiskAttachments;
1696 list< ComPtr<IHardDisk> > llHardDisksCreated;
1697 list<Guid> llMachinesRegistered;
1698
1699 ComPtr<ISession> session;
1700 bool fSessionOpen = false;
1701 rc = session.createInprocObject(CLSID_Session);
1702 CheckComRCReturnRC(rc);
1703
1704 list<VirtualSystem>::const_iterator it;
1705 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it1;
1706 /* Iterate through all virtual systems of that appliance */
1707 size_t i = 0;
1708 for (it = pAppliance->m->llVirtualSystems.begin(),
1709 it1 = pAppliance->m->virtualSystemDescriptions.begin();
1710 it != pAppliance->m->llVirtualSystems.end();
1711 ++it, ++it1, ++i)
1712 {
1713 const VirtualSystem &vsysThis = *it;
1714 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it1);
1715
1716 ComPtr<IMachine> pNewMachine;
1717
1718 /* Catch possible errors */
1719 try
1720 {
1721 /* Guest OS type */
1722 std::list<VirtualSystemDescriptionEntry*> vsdeOS;
1723 vsdeOS = vsdescThis->findByType(VirtualSystemDescriptionType_OS);
1724 if (vsdeOS.size() < 1)
1725 throw setError(VBOX_E_FILE_ERROR,
1726 tr("Missing guest OS type"));
1727 const Utf8Str &strOsTypeVBox = vsdeOS.front()->strVbox;
1728
1729 /* Now that we know the base system get our internal defaults based on that. */
1730 ComPtr<IGuestOSType> osType;
1731 rc = pVirtualBox->GetGuestOSType(Bstr(strOsTypeVBox), osType.asOutParam());
1732 if (FAILED(rc)) throw rc;
1733
1734 /* Create the machine */
1735 /* First get the name */
1736 std::list<VirtualSystemDescriptionEntry*> vsdeName = vsdescThis->findByType(VirtualSystemDescriptionType_Name);
1737 if (vsdeName.size() < 1)
1738 throw setError(VBOX_E_FILE_ERROR,
1739 tr("Missing VM name"));
1740 const Utf8Str &strNameVBox = vsdeName.front()->strVbox;
1741 rc = pVirtualBox->CreateMachine(Bstr(strNameVBox), Bstr(strOsTypeVBox),
1742 Bstr(), Guid(),
1743 pNewMachine.asOutParam());
1744 if (FAILED(rc)) throw rc;
1745
1746 // and the description
1747 std::list<VirtualSystemDescriptionEntry*> vsdeDescription = vsdescThis->findByType(VirtualSystemDescriptionType_Description);
1748 if (vsdeDescription.size())
1749 {
1750 const Utf8Str &strDescription = vsdeDescription.front()->strVbox;
1751 rc = pNewMachine->COMSETTER(Description)(Bstr(strDescription));
1752 if (FAILED(rc)) throw rc;
1753 }
1754
1755 /* CPU count (ignored for now) */
1756 // EntriesList vsdeCPU = vsd->findByType (VirtualSystemDescriptionType_CPU);
1757
1758 /* RAM */
1759 std::list<VirtualSystemDescriptionEntry*> vsdeRAM = vsdescThis->findByType(VirtualSystemDescriptionType_Memory);
1760 ComAssertMsgThrow(vsdeRAM.size() == 1, ("RAM size missing"), E_FAIL);
1761 const Utf8Str &memoryVBox = vsdeRAM.front()->strVbox;
1762 ULONG tt = (ULONG)RTStrToUInt64(memoryVBox.c_str());
1763 rc = pNewMachine->COMSETTER(MemorySize)(tt);
1764 if (FAILED(rc)) throw rc;
1765
1766 /* VRAM */
1767 /* Get the recommended VRAM for this guest OS type */
1768 ULONG vramVBox;
1769 rc = osType->COMGETTER(RecommendedVRAM)(&vramVBox);
1770 if (FAILED(rc)) throw rc;
1771
1772 /* Set the VRAM */
1773 rc = pNewMachine->COMSETTER(VRAMSize)(vramVBox);
1774 if (FAILED(rc)) throw rc;
1775
1776 /* Audio Adapter */
1777 std::list<VirtualSystemDescriptionEntry*> vsdeAudioAdapter = vsdescThis->findByType(VirtualSystemDescriptionType_SoundCard);
1778 /* @todo: we support one audio adapter only */
1779 if (vsdeAudioAdapter.size() > 0)
1780 {
1781 const Utf8Str& audioAdapterVBox = vsdeAudioAdapter.front()->strVbox;
1782 if (audioAdapterVBox.compare("null", Utf8Str::CaseInsensitive) != 0)
1783 {
1784 uint32_t audio = RTStrToUInt32(audioAdapterVBox.c_str());
1785 ComPtr<IAudioAdapter> audioAdapter;
1786 rc = pNewMachine->COMGETTER(AudioAdapter)(audioAdapter.asOutParam());
1787 if (FAILED(rc)) throw rc;
1788 rc = audioAdapter->COMSETTER(Enabled)(true);
1789 if (FAILED(rc)) throw rc;
1790 rc = audioAdapter->COMSETTER(AudioController)(static_cast<AudioControllerType_T>(audio));
1791 if (FAILED(rc)) throw rc;
1792 }
1793 }
1794
1795#ifdef VBOX_WITH_USB
1796 /* USB Controller */
1797 std::list<VirtualSystemDescriptionEntry*> vsdeUSBController = vsdescThis->findByType(VirtualSystemDescriptionType_USBController);
1798 // USB support is enabled if there's at least one such entry; to disable USB support,
1799 // the type of the USB item would have been changed to "ignore"
1800 bool fUSBEnabled = vsdeUSBController.size() > 0;
1801
1802 ComPtr<IUSBController> usbController;
1803 rc = pNewMachine->COMGETTER(USBController)(usbController.asOutParam());
1804 if (FAILED(rc)) throw rc;
1805 rc = usbController->COMSETTER(Enabled)(fUSBEnabled);
1806 if (FAILED(rc)) throw rc;
1807#endif /* VBOX_WITH_USB */
1808
1809 /* Change the network adapters */
1810 std::list<VirtualSystemDescriptionEntry*> vsdeNW = vsdescThis->findByType(VirtualSystemDescriptionType_NetworkAdapter);
1811 if (vsdeNW.size() == 0)
1812 {
1813 /* No network adapters, so we have to disable our default one */
1814 ComPtr<INetworkAdapter> nwVBox;
1815 rc = pNewMachine->GetNetworkAdapter(0, nwVBox.asOutParam());
1816 if (FAILED(rc)) throw rc;
1817 rc = nwVBox->COMSETTER(Enabled)(false);
1818 if (FAILED(rc)) throw rc;
1819 }
1820 else
1821 {
1822 list<VirtualSystemDescriptionEntry*>::const_iterator nwIt;
1823 /* Iterate through all network cards. We support 8 network adapters
1824 * at the maximum. (@todo: warn if there are more!) */
1825 size_t a = 0;
1826 for (nwIt = vsdeNW.begin();
1827 (nwIt != vsdeNW.end() && a < SchemaDefs::NetworkAdapterCount);
1828 ++nwIt, ++a)
1829 {
1830 const VirtualSystemDescriptionEntry* pvsys = *nwIt;
1831
1832 const Utf8Str &nwTypeVBox = pvsys->strVbox;
1833 uint32_t tt1 = RTStrToUInt32(nwTypeVBox.c_str());
1834 ComPtr<INetworkAdapter> pNetworkAdapter;
1835 rc = pNewMachine->GetNetworkAdapter((ULONG)a, pNetworkAdapter.asOutParam());
1836 if (FAILED(rc)) throw rc;
1837 /* Enable the network card & set the adapter type */
1838 rc = pNetworkAdapter->COMSETTER(Enabled)(true);
1839 if (FAILED(rc)) throw rc;
1840 rc = pNetworkAdapter->COMSETTER(AdapterType)(static_cast<NetworkAdapterType_T>(tt1));
1841 if (FAILED(rc)) throw rc;
1842
1843 // default is NAT; change to "bridged" if extra conf says so
1844 if (!pvsys->strExtraConfig.compare("type=Bridged", Utf8Str::CaseInsensitive))
1845 {
1846 /* Attach to the right interface */
1847 rc = pNetworkAdapter->AttachToBridgedInterface();
1848 if (FAILED(rc)) throw rc;
1849 ComPtr<IHost> host;
1850 rc = pVirtualBox->COMGETTER(Host)(host.asOutParam());
1851 if (FAILED(rc)) throw rc;
1852 com::SafeIfaceArray<IHostNetworkInterface> nwInterfaces;
1853 rc = host->COMGETTER(NetworkInterfaces)(ComSafeArrayAsOutParam(nwInterfaces));
1854 if (FAILED(rc)) throw rc;
1855 /* We search for the first host network interface which
1856 * is usable for bridged networking */
1857 for (size_t i=0; i < nwInterfaces.size(); ++i)
1858 {
1859 HostNetworkInterfaceType_T itype;
1860 rc = nwInterfaces[i]->COMGETTER(InterfaceType)(&itype);
1861 if (FAILED(rc)) throw rc;
1862 if (itype == HostNetworkInterfaceType_Bridged)
1863 {
1864 Bstr name;
1865 rc = nwInterfaces[i]->COMGETTER(Name)(name.asOutParam());
1866 if (FAILED(rc)) throw rc;
1867 /* Set the interface name to attach to */
1868 pNetworkAdapter->COMSETTER(HostInterface)(name);
1869 if (FAILED(rc)) throw rc;
1870 break;
1871 }
1872 }
1873 }
1874 /* Next test for host only interfaces */
1875 else if (!pvsys->strExtraConfig.compare("type=HostOnly", Utf8Str::CaseInsensitive))
1876 {
1877 /* Attach to the right interface */
1878 rc = pNetworkAdapter->AttachToHostOnlyInterface();
1879 if (FAILED(rc)) throw rc;
1880 ComPtr<IHost> host;
1881 rc = pVirtualBox->COMGETTER(Host)(host.asOutParam());
1882 if (FAILED(rc)) throw rc;
1883 com::SafeIfaceArray<IHostNetworkInterface> nwInterfaces;
1884 rc = host->COMGETTER(NetworkInterfaces)(ComSafeArrayAsOutParam(nwInterfaces));
1885 if (FAILED(rc)) throw rc;
1886 /* We search for the first host network interface which
1887 * is usable for host only networking */
1888 for (size_t i=0; i < nwInterfaces.size(); ++i)
1889 {
1890 HostNetworkInterfaceType_T itype;
1891 rc = nwInterfaces[i]->COMGETTER(InterfaceType)(&itype);
1892 if (FAILED(rc)) throw rc;
1893 if (itype == HostNetworkInterfaceType_HostOnly)
1894 {
1895 Bstr name;
1896 rc = nwInterfaces[i]->COMGETTER(Name)(name.asOutParam());
1897 if (FAILED(rc)) throw rc;
1898 /* Set the interface name to attach to */
1899 pNetworkAdapter->COMSETTER(HostInterface)(name);
1900 if (FAILED(rc)) throw rc;
1901 break;
1902 }
1903 }
1904 }
1905 }
1906 }
1907
1908 /* Floppy drive */
1909 std::list<VirtualSystemDescriptionEntry*> vsdeFloppy = vsdescThis->findByType(VirtualSystemDescriptionType_Floppy);
1910 // Floppy support is enabled if there's at least one such entry; to disable floppy support,
1911 // the type of the floppy item would have been changed to "ignore"
1912 bool fFloppyEnabled = vsdeFloppy.size() > 0;
1913 ComPtr<IFloppyDrive> floppyDrive;
1914 rc = pNewMachine->COMGETTER(FloppyDrive)(floppyDrive.asOutParam());
1915 if (FAILED(rc)) throw rc;
1916 rc = floppyDrive->COMSETTER(Enabled)(fFloppyEnabled);
1917 if (FAILED(rc)) throw rc;
1918
1919 /* CDROM drive */
1920 /* @todo: I can't disable the CDROM. So nothing to do for now */
1921 // std::list<VirtualSystemDescriptionEntry*> vsdeFloppy = vsd->findByType(VirtualSystemDescriptionType_CDROM);
1922
1923 /* Hard disk controller IDE */
1924 std::list<VirtualSystemDescriptionEntry*> vsdeHDCIDE = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerIDE);
1925 if (vsdeHDCIDE.size() > 1)
1926 throw setError(VBOX_E_FILE_ERROR,
1927 tr("Too many IDE controllers in OVF; VirtualBox only supports one"));
1928 if (vsdeHDCIDE.size() == 1)
1929 {
1930 ComPtr<IStorageController> pController;
1931 rc = pNewMachine->GetStorageControllerByName(Bstr("IDE"), pController.asOutParam());
1932 if (FAILED(rc)) throw rc;
1933
1934 const char *pcszIDEType = vsdeHDCIDE.front()->strVbox.c_str();
1935 if (!strcmp(pcszIDEType, "PIIX3"))
1936 rc = pController->COMSETTER(ControllerType)(StorageControllerType_PIIX3);
1937 else if (!strcmp(pcszIDEType, "PIIX4"))
1938 rc = pController->COMSETTER(ControllerType)(StorageControllerType_PIIX4);
1939 else if (!strcmp(pcszIDEType, "ICH6"))
1940 rc = pController->COMSETTER(ControllerType)(StorageControllerType_ICH6);
1941 else
1942 throw setError(VBOX_E_FILE_ERROR,
1943 tr("Invalid IDE controller type \"%s\""),
1944 pcszIDEType);
1945 if (FAILED(rc)) throw rc;
1946 }
1947#ifdef VBOX_WITH_AHCI
1948 /* Hard disk controller SATA */
1949 std::list<VirtualSystemDescriptionEntry*> vsdeHDCSATA = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerSATA);
1950 if (vsdeHDCSATA.size() > 1)
1951 throw setError(VBOX_E_FILE_ERROR,
1952 tr("Too many SATA controllers in OVF; VirtualBox only supports one"));
1953 if (vsdeHDCSATA.size() > 0)
1954 {
1955 ComPtr<IStorageController> pController;
1956 const Utf8Str &hdcVBox = vsdeHDCSATA.front()->strVbox;
1957 if (hdcVBox == "AHCI")
1958 {
1959 rc = pNewMachine->AddStorageController(Bstr("SATA"), StorageBus_SATA, pController.asOutParam());
1960 if (FAILED(rc)) throw rc;
1961 }
1962 else
1963 throw setError(VBOX_E_FILE_ERROR,
1964 tr("Invalid SATA controller type \"%s\""),
1965 hdcVBox.c_str());
1966 }
1967#endif /* VBOX_WITH_AHCI */
1968
1969 /* Hard disk controller SCSI */
1970 std::list<VirtualSystemDescriptionEntry*> vsdeHDCSCSI = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerSCSI);
1971 if (vsdeHDCSCSI.size() > 1)
1972 throw setError(VBOX_E_FILE_ERROR,
1973 tr("Too many SCSI controllers in OVF; VirtualBox only supports one"));
1974 if (vsdeHDCSCSI.size() > 0)
1975 {
1976 ComPtr<IStorageController> pController;
1977 StorageControllerType_T controllerType;
1978 const Utf8Str &hdcVBox = vsdeHDCSCSI.front()->strVbox;
1979 if (hdcVBox == "LsiLogic")
1980 controllerType = StorageControllerType_LsiLogic;
1981 else if (hdcVBox == "BusLogic")
1982 controllerType = StorageControllerType_BusLogic;
1983 else
1984 throw setError(VBOX_E_FILE_ERROR,
1985 tr("Invalid SCSI controller type \"%s\""),
1986 hdcVBox.c_str());
1987
1988 rc = pNewMachine->AddStorageController(Bstr("SCSI"), StorageBus_SCSI, pController.asOutParam());
1989 if (FAILED(rc)) throw rc;
1990 rc = pController->COMSETTER(ControllerType)(controllerType);
1991 if (FAILED(rc)) throw rc;
1992 }
1993
1994 /* Now its time to register the machine before we add any hard disks */
1995 rc = pVirtualBox->RegisterMachine(pNewMachine);
1996 if (FAILED(rc)) throw rc;
1997
1998 Guid newMachineId;
1999 rc = pNewMachine->COMGETTER(Id)(newMachineId.asOutParam());
2000 if (FAILED(rc)) throw rc;
2001
2002 // store new machine for roll-back in case of errors
2003 llMachinesRegistered.push_back(newMachineId);
2004
2005 /* Create the hard disks & connect them to the appropriate controllers. */
2006 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);
2007 if (avsdeHDs.size() > 0)
2008 {
2009 /* If in the next block an error occur we have to deregister
2010 the machine, so make an extra try/catch block. */
2011 ComPtr<IHardDisk> srcHdVBox;
2012 bool fSourceHdNeedsClosing = false;
2013
2014 try
2015 {
2016 /* In order to attach hard disks we need to open a session
2017 * for the new machine */
2018 rc = pVirtualBox->OpenSession(session, newMachineId);
2019 if (FAILED(rc)) throw rc;
2020 fSessionOpen = true;
2021
2022 /* The disk image has to be on the same place as the OVF file. So
2023 * strip the filename out of the full file path. */
2024 Utf8Str strSrcDir = stripFilename(pAppliance->m->strPath);
2025
2026 /* Iterate over all given disk images */
2027 list<VirtualSystemDescriptionEntry*>::const_iterator itHD;
2028 for (itHD = avsdeHDs.begin();
2029 itHD != avsdeHDs.end();
2030 ++itHD)
2031 {
2032 VirtualSystemDescriptionEntry *vsdeHD = *itHD;
2033
2034 const char *pcszDstFilePath = vsdeHD->strVbox.c_str();
2035 /* Check if the destination file exists already or the
2036 * destination path is empty. */
2037 if ( !(*pcszDstFilePath)
2038 || RTPathExists(pcszDstFilePath)
2039 )
2040 /* This isn't allowed */
2041 throw setError(VBOX_E_FILE_ERROR,
2042 tr("Destination file '%s' exists",
2043 pcszDstFilePath));
2044
2045 /* Find the disk from the OVF's disk list */
2046 DiskImagesMap::const_iterator itDiskImage = pAppliance->m->mapDisks.find(vsdeHD->strRef);
2047 /* vsdeHD->strRef contains the disk identifier (e.g. "vmdisk1"), which should exist
2048 in the virtual system's disks map under that ID and also in the global images map. */
2049 VirtualDisksMap::const_iterator itVirtualDisk = vsysThis.mapVirtualDisks.find(vsdeHD->strRef);
2050
2051 if ( itDiskImage == pAppliance->m->mapDisks.end()
2052 || itVirtualDisk == vsysThis.mapVirtualDisks.end()
2053 )
2054 throw setError(E_FAIL,
2055 tr("Internal inconsistency looking up disk images."));
2056
2057 const DiskImage &di = itDiskImage->second;
2058 const VirtualDisk &vd = itVirtualDisk->second;
2059
2060 /* Make sure all target directories exists */
2061 rc = VirtualBox::ensureFilePathExists(pcszDstFilePath);
2062 if (FAILED(rc))
2063 throw rc;
2064
2065 // subprogress object for hard disk
2066 ComPtr<IProgress> pProgress2;
2067
2068 ComPtr<IHardDisk> dstHdVBox;
2069 /* If strHref is empty we have to create a new file */
2070 if (di.strHref.isEmpty())
2071 {
2072 /* Which format to use? */
2073 Bstr srcFormat = L"VDI";
2074 if ( di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#sparse", Utf8Str::CaseInsensitive)
2075 || di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#compressed", Utf8Str::CaseInsensitive))
2076 srcFormat = L"VMDK";
2077 /* Create an empty hard disk */
2078 rc = pVirtualBox->CreateHardDisk(srcFormat, Bstr(pcszDstFilePath), dstHdVBox.asOutParam());
2079 if (FAILED(rc)) throw rc;
2080
2081 /* Create a dynamic growing disk image with the given capacity */
2082 rc = dstHdVBox->CreateBaseStorage(di.iCapacity / _1M, HardDiskVariant_Standard, pProgress2.asOutParam());
2083 if (FAILED(rc)) throw rc;
2084
2085 /* Advance to the next operation */
2086 if (!task->progress.isNull())
2087 task->progress->setNextOperation(BstrFmt(tr("Creating virtual disk image '%s'"), pcszDstFilePath),
2088 vsdeHD->ulSizeMB); // operation's weight, as set up with the IProgress originally
2089 }
2090 else
2091 {
2092 /* Construct the source file path */
2093 Utf8StrFmt strSrcFilePath("%s%c%s", strSrcDir.c_str(), RTPATH_DELIMITER, di.strHref.c_str());
2094 /* Check if the source file exists */
2095 if (!RTPathExists(strSrcFilePath.c_str()))
2096 /* This isn't allowed */
2097 throw setError(VBOX_E_FILE_ERROR,
2098 tr("Source virtual disk image file '%s' doesn't exist"),
2099 strSrcFilePath.c_str());
2100
2101 /* Clone the disk image (this is necessary cause the id has
2102 * to be recreated for the case the same hard disk is
2103 * attached already from a previous import) */
2104
2105 /* First open the existing disk image */
2106 rc = pVirtualBox->OpenHardDisk(Bstr(strSrcFilePath),
2107 AccessMode_ReadOnly,
2108 srcHdVBox.asOutParam());
2109 if (FAILED(rc)) throw rc;
2110 fSourceHdNeedsClosing = true;
2111
2112 /* We need the format description of the source disk image */
2113 Bstr srcFormat;
2114 rc = srcHdVBox->COMGETTER(Format)(srcFormat.asOutParam());
2115 if (FAILED(rc)) throw rc;
2116 /* Create a new hard disk interface for the destination disk image */
2117 rc = pVirtualBox->CreateHardDisk(srcFormat, Bstr(pcszDstFilePath), dstHdVBox.asOutParam());
2118 if (FAILED(rc)) throw rc;
2119 /* Clone the source disk image */
2120 rc = srcHdVBox->CloneTo(dstHdVBox, HardDiskVariant_Standard, pProgress2.asOutParam());
2121 if (FAILED(rc)) throw rc;
2122
2123 /* Advance to the next operation */
2124 if (!task->progress.isNull())
2125 task->progress->setNextOperation(BstrFmt(tr("Importing virtual disk image '%s'"), strSrcFilePath.c_str()),
2126 vsdeHD->ulSizeMB); // operation's weight, as set up with the IProgress originally);
2127 }
2128
2129 // now wait for the background disk operation to complete; this throws HRESULTs on error
2130 pAppliance->waitForAsyncProgress(task->progress, pProgress2);
2131
2132 if (fSourceHdNeedsClosing)
2133 {
2134 rc = srcHdVBox->Close();
2135 if (FAILED(rc)) throw rc;
2136 fSourceHdNeedsClosing = false;
2137 }
2138
2139 llHardDisksCreated.push_back(dstHdVBox);
2140 /* Now use the new uuid to attach the disk image to our new machine */
2141 ComPtr<IMachine> sMachine;
2142 rc = session->COMGETTER(Machine)(sMachine.asOutParam());
2143 if (FAILED(rc)) throw rc;
2144 Guid hdId;
2145 rc = dstHdVBox->COMGETTER(Id)(hdId.asOutParam());
2146 if (FAILED(rc)) throw rc;
2147
2148 /* For now we assume we have one controller of every type only */
2149 HardDiskController hdc = (*vsysThis.mapControllers.find(vd.idController)).second;
2150
2151 // this is for rollback later
2152 MyHardDiskAttachment mhda;
2153 mhda.uuid = newMachineId;
2154 mhda.pMachine = pNewMachine;
2155
2156 switch (hdc.system)
2157 {
2158 case HardDiskController::IDE:
2159 // For the IDE bus, the channel parameter can be either 0 or 1, to specify the primary
2160 // or secondary IDE controller, respectively. For the primary controller of the IDE bus,
2161 // the device number can be either 0 or 1, to specify the master or the slave device,
2162 // respectively. For the secondary IDE controller, the device number is always 1 because
2163 // the master device is reserved for the CD-ROM drive.
2164 mhda.controllerType = Bstr("IDE");
2165 switch (vd.ulAddressOnParent)
2166 {
2167 case 0: // interpret this as primary master
2168 mhda.lChannel = (long)0;
2169 mhda.lDevice = (long)0;
2170 break;
2171
2172 case 1: // interpret this as primary slave
2173 mhda.lChannel = (long)0;
2174 mhda.lDevice = (long)1;
2175 break;
2176
2177 case 2: // interpret this as secondary slave
2178 mhda.lChannel = (long)1;
2179 mhda.lDevice = (long)1;
2180 break;
2181
2182 default:
2183 throw setError(VBOX_E_NOT_SUPPORTED,
2184 tr("Invalid channel %RI16 specified; IDE controllers support only 0, 1 or 2"), vd.ulAddressOnParent);
2185 break;
2186 }
2187 break;
2188
2189 case HardDiskController::SATA:
2190 mhda.controllerType = Bstr("SATA");
2191 mhda.lChannel = (long)vd.ulAddressOnParent;
2192 mhda.lDevice = (long)0;
2193 break;
2194
2195 case HardDiskController::SCSI:
2196 mhda.controllerType = Bstr("SCSI");
2197 mhda.lChannel = (long)vd.ulAddressOnParent;
2198 mhda.lDevice = (long)0;
2199 break;
2200
2201 default: break;
2202 }
2203
2204 Log(("Attaching disk %s to channel %d on device %d\n", pcszDstFilePath, mhda.lChannel, mhda.lDevice));
2205
2206 rc = sMachine->AttachHardDisk(hdId,
2207 mhda.controllerType,
2208 mhda.lChannel,
2209 mhda.lDevice);
2210 if (FAILED(rc)) throw rc;
2211
2212 llHardDiskAttachments.push_back(mhda);
2213
2214 rc = sMachine->SaveSettings();
2215 if (FAILED(rc)) throw rc;
2216 } // end for (itHD = avsdeHDs.begin();
2217
2218 // only now that we're done with all disks, close the session
2219 rc = session->Close();
2220 if (FAILED(rc)) throw rc;
2221 fSessionOpen = false;
2222 }
2223 catch(HRESULT /* aRC */)
2224 {
2225 if (fSourceHdNeedsClosing)
2226 srcHdVBox->Close();
2227
2228 if (fSessionOpen)
2229 session->Close();
2230
2231 throw;
2232 }
2233 }
2234 }
2235 catch(HRESULT aRC)
2236 {
2237 rc = aRC;
2238 }
2239
2240 if (FAILED(rc))
2241 break;
2242
2243 } // for (it = pAppliance->m->llVirtualSystems.begin(),
2244
2245 if (FAILED(rc))
2246 {
2247 // with _whatever_ error we've had, do a complete roll-back of
2248 // machines and disks we've created; unfortunately this is
2249 // not so trivially done...
2250
2251 HRESULT rc2;
2252 // detach all hard disks from all machines we created
2253 list<MyHardDiskAttachment>::iterator itM;
2254 for (itM = llHardDiskAttachments.begin();
2255 itM != llHardDiskAttachments.end();
2256 ++itM)
2257 {
2258 const MyHardDiskAttachment &mhda = *itM;
2259 rc2 = pVirtualBox->OpenSession(session, mhda.uuid);
2260 if (SUCCEEDED(rc2))
2261 {
2262 ComPtr<IMachine> sMachine;
2263 rc2 = session->COMGETTER(Machine)(sMachine.asOutParam());
2264 if (SUCCEEDED(rc2))
2265 {
2266 rc2 = sMachine->DetachHardDisk(Bstr(mhda.controllerType), mhda.lChannel, mhda.lDevice);
2267 rc2 = sMachine->SaveSettings();
2268 }
2269 session->Close();
2270 }
2271 }
2272
2273 // now clean up all hard disks we created
2274 list< ComPtr<IHardDisk> >::iterator itHD;
2275 for (itHD = llHardDisksCreated.begin();
2276 itHD != llHardDisksCreated.end();
2277 ++itHD)
2278 {
2279 ComPtr<IHardDisk> pDisk = *itHD;
2280 ComPtr<IProgress> pProgress;
2281 rc2 = pDisk->DeleteStorage(pProgress.asOutParam());
2282 rc2 = pProgress->WaitForCompletion(-1);
2283 }
2284
2285 // finally, deregister and remove all machines
2286 list<Guid>::iterator itID;
2287 for (itID = llMachinesRegistered.begin();
2288 itID != llMachinesRegistered.end();
2289 ++itID)
2290 {
2291 const Guid &guid = *itID;
2292 ComPtr<IMachine> failedMachine;
2293 rc2 = pVirtualBox->UnregisterMachine(guid, failedMachine.asOutParam());
2294 if (SUCCEEDED(rc2))
2295 rc2 = failedMachine->DeleteSettings();
2296 }
2297 }
2298
2299 task->rc = rc;
2300
2301 if (!task->progress.isNull())
2302 task->progress->notifyComplete(rc);
2303
2304 LogFlowFunc(("rc=%Rhrc\n", rc));
2305 LogFlowFuncLeave();
2306
2307 return VINF_SUCCESS;
2308}
2309
2310struct Appliance::TaskWriteOVF
2311{
2312 TaskWriteOVF(Appliance *aThat, Progress *aProgress)
2313 : pAppliance(aThat)
2314 , progress(aProgress)
2315 , rc(S_OK)
2316 {}
2317 ~TaskWriteOVF() {}
2318
2319 HRESULT startThread();
2320
2321 Appliance *pAppliance;
2322 ComObjPtr<Progress> progress;
2323 HRESULT rc;
2324};
2325
2326HRESULT Appliance::TaskWriteOVF::startThread()
2327{
2328 int vrc = RTThreadCreate(NULL, Appliance::taskThreadWriteOVF, this,
2329 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
2330 "Appliance::Task");
2331 ComAssertMsgRCRet(vrc,
2332 ("Could not create taskThreadExportOVF (%Rrc)\n", vrc), E_FAIL);
2333
2334 return S_OK;
2335}
2336
2337STDMETHODIMP Appliance::Write(IN_BSTR path, IProgress **aProgress)
2338{
2339 HRESULT rc = S_OK;
2340
2341 CheckComArgOutPointerValid(aProgress);
2342
2343 AutoCaller autoCaller(this);
2344 if (FAILED(rc = autoCaller.rc())) return rc;
2345
2346 AutoWriteLock(this);
2347
2348 // see if we can handle this file; for now we insist it has an ".ovf" extension
2349 m->strPath = path;
2350 if (!m->strPath.endsWith(".ovf", Utf8Str::CaseInsensitive))
2351 return setError(VBOX_E_FILE_ERROR,
2352 tr("Appliance file must have .ovf extension"));
2353
2354 ComObjPtr<Progress> progress;
2355 try
2356 {
2357 Bstr progressDesc = BstrFmt(tr("Export appliance '%s'"),
2358 m->strPath.raw());
2359 rc = setUpProgress(progress, progressDesc);
2360 if (FAILED(rc)) throw rc;
2361
2362 /* Initialize our worker task */
2363 std::auto_ptr<TaskWriteOVF> task(new TaskWriteOVF(this, progress));
2364 //AssertComRCThrowRC (task->autoCaller.rc());
2365
2366 rc = task->startThread();
2367 CheckComRCThrowRC(rc);
2368
2369 task.release();
2370 }
2371 catch (HRESULT aRC)
2372 {
2373 rc = aRC;
2374 }
2375
2376 if (SUCCEEDED(rc))
2377 /* Return progress to the caller */
2378 progress.queryInterfaceTo(aProgress);
2379
2380 return rc;
2381}
2382
2383/**
2384 * Worker thread implementation for Write() (ovf writer).
2385 * @param aThread
2386 * @param pvUser
2387 */
2388/* static */
2389DECLCALLBACK(int) Appliance::taskThreadWriteOVF(RTTHREAD /* aThread */, void *pvUser)
2390{
2391 std::auto_ptr<TaskWriteOVF> task(static_cast<TaskWriteOVF*>(pvUser));
2392 AssertReturn(task.get(), VERR_GENERAL_FAILURE);
2393
2394 Appliance *pAppliance = task->pAppliance;
2395
2396 LogFlowFuncEnter();
2397 LogFlowFunc(("Appliance %p\n", pAppliance));
2398
2399 AutoCaller autoCaller(pAppliance);
2400 CheckComRCReturnRC(autoCaller.rc());
2401
2402 AutoWriteLock appLock(pAppliance);
2403
2404 HRESULT rc = S_OK;
2405
2406 ComPtr<IVirtualBox> pVirtualBox(pAppliance->mVirtualBox);
2407
2408 try
2409 {
2410 xml::Document doc;
2411 xml::ElementNode *pelmRoot = doc.createRootElement("Envelope");
2412
2413 pelmRoot->setAttribute("ovf:version", "1.0");
2414 pelmRoot->setAttribute("xml:lang", "en-US");
2415 pelmRoot->setAttribute("xmlns", "http://schemas.dmtf.org/ovf/envelope/1");
2416 pelmRoot->setAttribute("xmlns:ovf", "http://schemas.dmtf.org/ovf/envelope/1");
2417 pelmRoot->setAttribute("xmlns:ovfstr", "http://schema.dmtf.org/ovf/strings/1");
2418 pelmRoot->setAttribute("xmlns:rasd", "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData");
2419 pelmRoot->setAttribute("xmlns:vssd", "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData");
2420 pelmRoot->setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
2421 pelmRoot->setAttribute("xsi:schemaLocation", "http://schemas.dmtf.org/ovf/envelope/1 ../ovf-envelope.xsd");
2422
2423 // <Envelope>/<References>
2424 xml::ElementNode *pelmReferences = pelmRoot->createChild("References");
2425
2426 /* <Envelope>/<DiskSection>:
2427 <DiskSection>
2428 <Info>List of the virtual disks used in the package</Info>
2429 <Disk ovf:capacity="4294967296" ovf:diskId="lamp" ovf:format="http://www.vmware.com/specifications/vmdk.html#compressed" ovf:populatedSize="1924967692"/>
2430 </DiskSection> */
2431 xml::ElementNode *pelmDiskSection = pelmRoot->createChild("DiskSection");
2432 xml::ElementNode *pelmDiskSectionInfo = pelmDiskSection->createChild("Info");
2433 pelmDiskSectionInfo->addContent("List of the virtual disks used in the package");
2434 // for now, set up a map so we have a list of unique disk names (to make
2435 // sure the same disk name is only added once)
2436 map<Utf8Str, const VirtualSystemDescriptionEntry*> mapDisks;
2437
2438 /* <Envelope>/<NetworkSection>:
2439 <NetworkSection>
2440 <Info>Logical networks used in the package</Info>
2441 <Network ovf:name="VM Network">
2442 <Description>The network that the LAMP Service will be available on</Description>
2443 </Network>
2444 </NetworkSection> */
2445 xml::ElementNode *pelmNetworkSection = pelmRoot->createChild("NetworkSection");
2446 xml::ElementNode *pelmNetworkSectionInfo = pelmNetworkSection->createChild("Info");
2447 pelmNetworkSectionInfo->addContent("Logical networks used in the package");
2448 // for now, set up a map so we have a list of unique network names (to make
2449 // sure the same network name is only added once)
2450 map<Utf8Str, bool> mapNetworks;
2451 // we fill this later below when we iterate over the networks
2452
2453 // and here come the virtual systems:
2454 xml::ElementNode *pelmVirtualSystemCollection = pelmRoot->createChild("VirtualSystemCollection");
2455 /* xml::AttributeNode *pattrVirtualSystemCollectionId = */ pelmVirtualSystemCollection->setAttribute("ovf:id", "ExportedVirtualBoxMachines"); // whatever
2456
2457 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;
2458 /* Iterate through all virtual systems of that appliance */
2459 for (it = pAppliance->m->virtualSystemDescriptions.begin();
2460 it != pAppliance->m->virtualSystemDescriptions.end();
2461 ++it)
2462 {
2463 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);
2464
2465 xml::ElementNode *pelmVirtualSystem = pelmVirtualSystemCollection->createChild("VirtualSystem");
2466
2467 /*xml::ElementNode *pelmVirtualSystemInfo =*/ pelmVirtualSystem->createChild("Info")->addContent("A virtual machine");
2468
2469 std::list<VirtualSystemDescriptionEntry*> llName = vsdescThis->findByType(VirtualSystemDescriptionType_Name);
2470 if (llName.size() != 1)
2471 throw setError(VBOX_E_NOT_SUPPORTED,
2472 tr("Missing VM name"));
2473 pelmVirtualSystem->setAttribute("ovf:id", llName.front()->strVbox);
2474
2475 // description
2476 std::list<VirtualSystemDescriptionEntry*> llDescription = vsdescThis->findByType(VirtualSystemDescriptionType_Description);
2477 if (llDescription.size())
2478 {
2479 /* <Section ovf:required="false" xsi:type="ovf:AnnotationSection_Type">
2480 <Info>A human-readable annotation</Info>
2481 <Annotation>Plan 9</Annotation>
2482 </Section> */
2483 xml::ElementNode *pelmAnnotationSection = pelmVirtualSystem->createChild("AnnotationSection");
2484 pelmAnnotationSection->createChild("Info")->addContent("A human-readable annotation");
2485 pelmAnnotationSection->createChild("Annotation")->addContent(llDescription.front()->strVbox);
2486 }
2487
2488 // license
2489 std::list<VirtualSystemDescriptionEntry*> llLicense = vsdescThis->findByType(VirtualSystemDescriptionType_License);
2490 if (llLicense.size())
2491 {
2492 /* <EulaSection>
2493 <Info ovf:msgid="6">License agreement for the Virtual System.</Info>
2494 <License ovf:msgid="1">License terms can go in here.</License>
2495 </EulaSection> */
2496 xml::ElementNode *pelmAnnotationSection = pelmVirtualSystem->createChild("EulaSection");
2497 pelmAnnotationSection->createChild("Info")->addContent("License agreement for the Virtual System.");
2498 pelmAnnotationSection->createChild("License")->addContent(llLicense.front()->strVbox);
2499 }
2500
2501 // operating system
2502 std::list<VirtualSystemDescriptionEntry*> llOS = vsdescThis->findByType(VirtualSystemDescriptionType_OS);
2503 if (llOS.size() != 1)
2504 throw setError(VBOX_E_NOT_SUPPORTED,
2505 tr("Missing OS type"));
2506 /* <OperatingSystemSection ovf:id="82">
2507 <Info>Guest Operating System</Info>
2508 <Description>Linux 2.6.x</Description>
2509 </OperatingSystemSection> */
2510 xml::ElementNode *pelmOperatingSystemSection = pelmVirtualSystem->createChild("OperatingSystemSection");
2511 pelmOperatingSystemSection->setAttribute("ovf:id", llOS.front()->strOvf);
2512// pelmOperatingSystemSection->createChild("Info")->addContent("blah"); // @todo
2513// pelmOperatingSystemSection->createChild("Description")->addContent("blah"); // @todo
2514
2515 // <VirtualHardwareSection ovf:id="hw1" ovf:transport="iso">
2516 xml::ElementNode *pelmVirtualHardwareSection = pelmVirtualSystem->createChild("VirtualHardwareSection");
2517
2518 /* <System>
2519 <vssd:Description>Description of the virtual hardware section.</vssd:Description>
2520 <vssd:ElementName>vmware</vssd:ElementName>
2521 <vssd:InstanceID>1</vssd:InstanceID>
2522 <vssd:VirtualSystemIdentifier>MyLampService</vssd:VirtualSystemIdentifier>
2523 <vssd:VirtualSystemType>vmx-4</vssd:VirtualSystemType>
2524 </System> */
2525 xml::ElementNode *pelmSystem = pelmVirtualHardwareSection->createChild("System");
2526
2527 // <vssd:VirtualSystemType>vmx-4</vssd:VirtualSystemType>
2528 xml::ElementNode *pelmVirtualSystemType = pelmSystem->createChild("VirtualSystemType");
2529 pelmVirtualSystemType->addContent("virtualbox-2.2"); // instead of vmx-7?
2530
2531 // loop thru all description entries twice; once to write out all
2532 // devices _except_ disk images, and a second time to assign the
2533 // disk images; this is because disk images need to reference
2534 // IDE controllers, and we can't know their instance IDs without
2535 // assigning them first
2536
2537 uint32_t idIDEController = 0;
2538 int32_t lIDEControllerIndex = 0;
2539 uint32_t idSATAController = 0;
2540 int32_t lSATAControllerIndex = 0;
2541 uint32_t idSCSIController = 0;
2542 int32_t lSCSIControllerIndex = 0;
2543
2544 uint32_t ulInstanceID = 1;
2545 uint32_t cDisks = 0;
2546
2547 for (size_t uLoop = 1;
2548 uLoop <= 2;
2549 ++uLoop)
2550 {
2551 int32_t lIndexThis = 0;
2552 list<VirtualSystemDescriptionEntry>::const_iterator itD;
2553 for (itD = vsdescThis->m->llDescriptions.begin();
2554 itD != vsdescThis->m->llDescriptions.end();
2555 ++itD, ++lIndexThis)
2556 {
2557 const VirtualSystemDescriptionEntry &desc = *itD;
2558
2559 OVFResourceType_T type = (OVFResourceType_T)0; // if this becomes != 0 then we do stuff
2560 Utf8Str strResourceSubType;
2561
2562 Utf8Str strDescription; // results in <rasd:Description>...</rasd:Description> block
2563 Utf8Str strCaption; // results in <rasd:Caption>...</rasd:Caption> block
2564
2565 uint32_t ulParent = 0;
2566
2567 int32_t lVirtualQuantity = -1;
2568 Utf8Str strAllocationUnits;
2569
2570 int32_t lAddress = -1;
2571 int32_t lBusNumber = -1;
2572 int32_t lAddressOnParent = -1;
2573
2574 int32_t lAutomaticAllocation = -1; // 0 means "false", 1 means "true"
2575 Utf8Str strConnection; // results in <rasd:Connection>...</rasd:Connection> block
2576 Utf8Str strHostResource;
2577
2578 uint64_t uTemp;
2579
2580 switch (desc.type)
2581 {
2582 case VirtualSystemDescriptionType_CPU:
2583 /* <Item>
2584 <rasd:Caption>1 virtual CPU</rasd:Caption>
2585 <rasd:Description>Number of virtual CPUs</rasd:Description>
2586 <rasd:ElementName>virtual CPU</rasd:ElementName>
2587 <rasd:InstanceID>1</rasd:InstanceID>
2588 <rasd:ResourceType>3</rasd:ResourceType>
2589 <rasd:VirtualQuantity>1</rasd:VirtualQuantity>
2590 </Item> */
2591 if (uLoop == 1)
2592 {
2593 strDescription = "Number of virtual CPUs";
2594 type = OVFResourceType_Processor; // 3
2595 lVirtualQuantity = 1;
2596 }
2597 break;
2598
2599 case VirtualSystemDescriptionType_Memory:
2600 /* <Item>
2601 <rasd:AllocationUnits>MegaBytes</rasd:AllocationUnits>
2602 <rasd:Caption>256 MB of memory</rasd:Caption>
2603 <rasd:Description>Memory Size</rasd:Description>
2604 <rasd:ElementName>Memory</rasd:ElementName>
2605 <rasd:InstanceID>2</rasd:InstanceID>
2606 <rasd:ResourceType>4</rasd:ResourceType>
2607 <rasd:VirtualQuantity>256</rasd:VirtualQuantity>
2608 </Item> */
2609 if (uLoop == 1)
2610 {
2611 strDescription = "Memory Size";
2612 type = OVFResourceType_Memory; // 4
2613 desc.strVbox.toInt(uTemp);
2614 lVirtualQuantity = (int32_t)(uTemp / _1M);
2615 strAllocationUnits = "MegaBytes";
2616 }
2617 break;
2618
2619 case VirtualSystemDescriptionType_HardDiskControllerIDE:
2620 /* <Item>
2621 <rasd:Caption>ideController1</rasd:Caption>
2622 <rasd:Description>IDE Controller</rasd:Description>
2623 <rasd:InstanceId>5</rasd:InstanceId>
2624 <rasd:ResourceType>5</rasd:ResourceType>
2625 <rasd:Address>1</rasd:Address>
2626 <rasd:BusNumber>1</rasd:BusNumber>
2627 </Item> */
2628 if (uLoop == 1)
2629 {
2630 strDescription = "IDE Controller";
2631 type = OVFResourceType_IDEController; // 5
2632 strResourceSubType = desc.strVbox;
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