VirtualBox

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

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

API/Machine+SystemProperties: get rid of the tri-state bool controlling hwvirtex

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

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