VirtualBox

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

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

Main/Appliance: windows build fix

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