VirtualBox

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

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

OVF: enable the I/O APIC on import of Windows machines.

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

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