VirtualBox

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

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

Main: adpated to paramter type change of COMGETTER(ResultCode)

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