VirtualBox

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

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

Main-S3: initial code for the export of OVF's to an S3 server

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

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