VirtualBox

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

Last change on this file since 19610 was 19519, checked in by vboxsync, 15 years ago

Main: decouple xml classes from COM dependencies

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