VirtualBox

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

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

Main-S3: backed out r47779 because of Win (mkdtemp)

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